Web 安全之 CSRF 攻击

简介: Web 安全之 CSRF 攻击

CSRF 攻击中文为跨站请求伪造,全拼为 Cross-site request forgery,也常被称为 XSRF 攻击。虽然 CSRF 攻击没有 XSS 攻击更常见,但其破坏性同样不容小觑。

攻击原理

CSRF 攻击是攻击者利用目标网站对用户浏览器的信任,对已登录用户执行非用户本意的操作。

比如用户登录了 A 网站,记录用户登录的会话信息就会以 Cookie 的形式保存在浏览器中,此时用户访问攻击者提供的 B 网站,B 网站就可以在用户不知情的情况下,伪造一个请求发送给 A 网站的服务器,浏览器会自动在这个请求中携带 Cookie,A 网站的服务器收到这个请求,以为这个请求是 A 网站已登录的用户自己发送过来的,于是执行相应的操作(如发邮件、转账等),就此实现攻击。

攻击示例

这里我用 Flask 编写两个简单的 Web 应用来作为 CSRF 攻击的示例。执行以下代码之前你需要使用 pip install flask 的方式安装 Flask

首先编写一个带有转账功能的 Web 应用,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# demo/app.py
from flask import Flask, request, make_response, render_template, redirect
app = Flask(__name__)
@app.route('/')
defindex():
"""首页"""
    session_id = request.cookies.get('session_id')
if session_id:
        is_login = '已登录'
else:
        is_login = '未登录'
return render_template('index.html', is_login=is_login)
@app.route('/login')
deflogin():
"""登录"""
    response = make_response(redirect('/'))
    response.set_cookie('session_id', '025fcb1587eea0a25493653dfcecc6f1')
return response
@app.route('/transfer', methods=['POST'])
deftransfer():
"""转账"""
    session_id = request.cookies.get('session_id')
    target_user = request.form.get('target_user')
if session_id:
returnf'给用户{target_user}转账成功'
else:
return'用户未登录'
if __name__ == '__main__':
    app.run(port=5000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- demo/templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8">
<title>Index</title>
</head>
<body>
是否登录:{{ is_login }}
<formaction="/transfer"method="post">
    接收用户:<inputtype="text"name="target_user">
<button>转账</button>
</form>
</body>
</html>

这个 Web 应用中包含了三个视图函数,分别为首页、登录和转账,程序监听 5000 端口。

应用逻辑比较简单,index 视图函数通过判断请求中是否存在键为 session_idCookie 信息来确认用户是否登录。login 视图函数用来进行登录操作,其实就是给用户浏览器设置一个键为 session_idCookie 信息,登录成功后重定向到首页。transfer 视图函数用来实现转账功能,只接受 POST 请求,判断用户是否登录的逻辑与 index 视图函数相同,已登录返回转账成功信息,未登录则返回 用户未登录

应用只包含了一个 index.html 页面,登录后即可通过 form 表单的形式进行转账操作。

虽然这个 Web 应用看起来比较傻,但足以演示 CSRF 攻击过程。启动 Flask 应用,使用浏览器访问 http://127.0.0.1:5000/ 即可看到首页内容。

此时在接收用户的输入框中输入 张三,点击 转账 按钮,会提示 用户未登录

 

那么我们可以先访问 http://127.0.0.1:5000/login 进行登录操作。

 

登录成功后,再次进行转账操作即可成功。

 

接下来站在攻击者的角度,编写一个能够对这个转账应用进行攻击的 Web 应用,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# demo_csrf/app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
defindex():
return render_template('index.html')
if __name__ == '__main__':
    app.run(port=5001)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- demo_csrf/templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8">
<title>CSRF</title>
</head>
<body>
Hello World!
<formaction="http://127.0.0.1:5000/transfer"method="post">
<inputhiddentype="text"name="target_user"value="张三">
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>

这个 Web 应用中只包含一个首页视图函数,程序监听 5001 端口。

index.html 页面中,有一句 Hello World! 和一个 form 表单。由于 form 表单中的 input 输入框带有 hidden 属性,所以不会显示在页面中。页面中还有一个 <script> 标签,包含一句 JavaScript 代码 document.forms[0].submit();,其作用是提交 form 表单。

启动这个具有攻击效果的 Flask 应用,在刚刚已经成功登录到 http://127.0.0.1:5000/ 的浏览器中访问 http://192.168.3.14:5001/(为了模拟不同域名,所以这里使用了内网 IP,如果你想在自己的电脑上运行程序则需要改为自己电脑的内网 IP)。

 

页面显示的内容一瞬间就从 Hello World! 变成了 给用户张三转账成功,此时攻击已经完成。

打开浏览器的开发者工具查看请求过程,可以发现浏览器共发送了两个请求。

第一个请求即为在浏览器地址栏中访问 http://192.168.3.14:5001/,第二个请求则是通过 JavaScript 代码自动提交的 form 表单请求 http://127.0.0.1:5000/transfer,并且浏览器自动携带了 Cookie 信息。

总结一下攻击流程:

  1. 用户访问目标网站 http://127.0.0.1:5000/ 并进行登录。
  2. 用户访问攻击者网站 http://192.168.3.14:5001/
  3. 攻击者网站 http://192.168.3.14:5001/ 通过 JavaScript 代码向 http://127.0.0.1:5000/transfer 发送转账请求,此时浏览器会自动携带 http://127.0.0.1:5000/ 下的 Cookie 信息。
  4. http://127.0.0.1:5000/ 网站的后台服务器接收到攻击者发送过来的请求,通过 Cookie 信息验证用户身份通过,完成转账操作。

当然在现实场景,用户不会主动去访问攻击者提供的网站。但攻击者会想尽各种办法诱导用户去访问。比如攻击者在给目标网站的留言中携带网站链接、在给用户发送的邮件中携带链接等。

CSRF 攻击不止这一种方式,还可以通过 a标签的 href 属性、img 标签的 src 属性等。

防范方法

CSRF 攻击的本质是在攻击者网站发送的跨站请求中,浏览器自动携带了 Cookie 信息,而浏览器和网站服务器不具备验证这个请求是否为用户自愿发起的请求的能力。

所以防范方法就是在访问敏感数据请求时,要求用户浏览器提供不保存在 Cookie 中的,并且攻击者无法伪造的数据作为校验,这样 CSRF 攻击就无法完成。

根据以上分析修改这个带有转账功能的 Web 应用,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# demo/app.py
import uuid
from flask import Flask, request, make_response, render_template, redirect
app = Flask(__name__)
csrftoken = uuid.uuid4().hex
@app.route('/')
defindex():
"""首页"""
    session_id = request.cookies.get('session_id')
if session_id:
        is_login = '已登录'
else:
        is_login = '未登录'
return render_template('index.html', is_login=is_login, csrftoken=csrftoken)
@app.route('/login')
deflogin():
"""登录"""
    response = make_response(redirect('/'))
    response.set_cookie('session_id', '025fcb1587eea0a25493653dfcecc6f1')
return response
@app.route('/transfer', methods=['POST'])
deftransfer():
"""转账"""
    session_id = request.cookies.get('session_id')
    target_user = request.form.get('target_user')
    token = request.form.get('csrftoken')
if session_id:
if csrftoken != token:
return'csrftoken验证失败'
returnf'给用户{target_user}转账成功'
else:
return'用户未登录'
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- demo/templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8">
<title>Index</title>
</head>
<body>
是否登录:{{ is_login }}
<formaction="/transfer"method="post">
    接收用户:<inputtype="text"name="target_user">
<inputhiddentype="text"value="{{ csrftoken }}">
<button>转账</button>
</form>
</body>
</html>

Flask 应用启动时,生成一个随机的 csrftoken 用来校验请求发起方。当用户访问首页,这个 csrftoken 会被传递到 index.html 中,并且作为 form 表单中一个 input 标签的 value 值。为了不影响页面原有数据展示,将这个 input 标签隐藏了起来(带有 hidden 属性)。当提交 form 表单时 csrftoken 会随表单数据一同被提交。在 transfer 视图函数中从表单数据获取 csrftoken 值,对比内存中已有的 csrftoken 值,如果两者相同则说明该请求是用户发起的,如果两者不同则说明请求是攻击者伪造的跨站请求。因为攻击者无法在自己的网站中构造出这个 csrftoken 值,所以攻击者网站提交的 form 表单数据中无法携带有效的 csrftoken,这样就能够有效的防范 CSRF 攻击。

现在再次尝试访问 http://192.168.3.14:5001/,攻击将无法完成。

可以看到,即使用户已经登录了,但 csrftoken 校验失败,依然无法转账成功。

虽然我们的 Web 应用有效的防范了 CSRF 攻击,但这个实现并不完美。在生产环境的项目中,我们还需要考虑 csrftoken 的时效性,使其在一定时间内过期,并且使用过一次的 csrftoken 也要做失效处理。不过别担心,这些功能早有人帮我们实现了。在 Flask 项目中我们可以通过第三方扩展来实现,如 Flask-WTF 内置的 CSRFProtectDjango 框架甚至默认集成了此功能。

实际上还有很多其他方法可以用来防范 CSRF 攻击,如同源检测,服务端检查 HTTP 请求头中的 OriginReferer 字段,再比如使用 CookieSameSite 属性来限制第三方 Cookie

每种防范机制都有各自的优缺点,使用哪种机制应该结合 Web 应用程序情况综合考量,一般来说我们这里举例用的 csrftoken 机制是比较通用的做法。

此外,我们还需要注意程序中的 XSS 漏洞。如果网站中存在 XSS 漏洞,攻击者同样可以通过 XSS 攻击的方式先拿到 csrftoken,然后再进行 CSRF 攻击。

当然,所谓安全都是相对的,并没有绝对的安全,对敏感数据的操作,多加一层校验,虽然看似麻烦,但总好过亡羊补牢。

相关文章
|
8天前
|
缓存 安全 搜索推荐
阿里云先知安全沙龙(北京站) ——浅谈Web快速打点
信息收集是网络安全中的重要环节,常用工具如Hunter、Fofa和扫描工具可帮助全面了解目标系统的网络结构与潜在漏洞。遇到默认Nginx或Tomcat 404页面时,可通过扫路径、域名模糊测试、搜索引擎缓存等手段获取更多信息。AllIN工具(GitHub: P1-Team/AllIN)能高效扫描网站路径,发现敏感信息。漏洞利用则需充分准备,以应对突发情况,确保快速拿下目标站点。 简介:信息收集与漏洞利用是网络安全的两大关键步骤。通过多种工具和技术手段,安全人员可以全面了解目标系统,发现潜在漏洞,并制定有效的防御和攻击策略。
|
17天前
|
安全 应用服务中间件 网络安全
实战经验分享:利用免费SSL证书构建安全可靠的Web应用
本文分享了利用免费SSL证书构建安全Web应用的实战经验,涵盖选择合适的证书颁发机构、申请与获取证书、配置Web服务器、优化安全性及实际案例。帮助开发者提升应用安全性,增强用户信任。
|
2月前
|
安全 前端开发 JavaScript
什么是 CSRF 攻击?如何启用 CSRF 保护来抵御该攻击?
什么是 CSRF 攻击?如何启用 CSRF 保护来抵御该攻击?
91 5
|
2月前
|
Web App开发 网络协议 安全
基于Web攻击的方式发现并攻击物联网设备介绍
基于Web攻击的方式发现并攻击物联网设备介绍
45 4
|
2月前
|
存储 Web App开发 安全
如何防范 CSRF 攻击
CSRF(跨站请求伪造)攻击是一种常见的安全威胁。防范措施包括:使用Anti-CSRF Token、检查HTTP Referer、限制Cookie作用域、采用双重提交Cookie机制等,确保请求的合法性与安全性。
|
2月前
|
网络安全 数据安全/隐私保护
什么是 CSRF 攻击
CSRF(跨站请求伪造)攻击是指攻击者诱导用户点击恶意链接或提交表单,利用用户已登录的身份在目标网站上执行非授权操作,如转账、修改密码等。这种攻击通常通过嵌入恶意代码或链接实现。
|
2月前
|
SQL 负载均衡 安全
安全至上:Web应用防火墙技术深度剖析与实战
【10月更文挑战第29天】在数字化时代,Web应用防火墙(WAF)成为保护Web应用免受攻击的关键技术。本文深入解析WAF的工作原理和核心组件,如Envoy和Coraza,并提供实战指南,涵盖动态加载规则、集成威胁情报、高可用性配置等内容,帮助开发者和安全专家构建更安全的Web环境。
80 1
|
2月前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
90 2
|
2月前
|
SQL 存储 安全
什么是XSS攻击?什么是SQL注入攻击?什么是CSRF攻击?
理解并防范XSS、SQL注入和CSRF攻击是Web应用安全的基础。通过采用严格的输入验证、使用安全编码实践以及实现适当的身份验证和授权机制,可以有效防止这些常见的Web攻击,保障应用程序和用户的数据安全。
46 0
|
3月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
175 3