什么是CSRF


本文作者: jsweibo

本文链接: https://jsweibo.github.io/2020/02/21/%E4%BB%80%E4%B9%88%E6%98%AFCSRF/

摘要

本文主要讲述了:

  1. 什么是 CSRF
  2. 根本矛盾
  3. 防御措施

正文

什么是 CSRF

Cross-site request forgery(跨站请求伪造),简称为 CSRF。由于 Cross 在英语中也有交叉的意思,因此人们用 X 来取代 C,有时也将其简称为 XSRF。

示例:

a.example.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!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>Document</title>
</head>
<body>
<script>
document.cookie = 'userId=123';
</script>
</body>
</html>

a.foo.com

1
2
3
4
5
6
7
8
9
10
11
12
<!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>Document</title>
</head>
<body>
<img src="//a.example.com/transferMoney?to=jsweibo&amount=1000" />
</body>
</html>

用户首先登录a.example.com,而后访问a.foo.com。此时网页会自动发送请求。由于a.example.com的 cookie 已存在于浏览器中,此时访问a.example.com/transferMoney?to=jsweibo&amount=1000会自动带上a.example.com的 cookie。而后用户在a.example.com的资金被盗。

示例:

a.example.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!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>Document</title>
</head>
<body>
<script>
document.cookie = 'userId=123';
</script>
</body>
</html>

a.foo.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!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>Document</title>
</head>
<body>
<iframe name="foo" hidden></iframe>
<form
id="bar"
method="POST"
action="//a.example.com/transferMoney"
target="foo"
>
<input type="hidden" name="to" value="jsweibo" />
<input type="hidden" name="amount" value="1000" />
</form>
<script>
document.querySelector('#bar').submit();
</script>
</body>
</html>

用户首先登录a.example.com,而后访问a.foo.com。此时网页会自动提交表单。由于a.example.com的 cookie 已存在于浏览器中,此时访问a.example.com/transferMoney会自动带上a.example.com的 cookie。而后用户在a.example.com的资金被盗。

示例:

a.example.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!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>Document</title>
</head>
<body>
<script>
document.cookie = 'userId=123';
</script>
</body>
</html>

a.foo.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!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>Document</title>
</head>
<body>
<script>
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open('POST', '//a.example.com/transferMoney');
xhr.send('to=jsweibo&amout=1000');
</script>
</body>
</html>

用户首先登录a.example.com,而后访问a.foo.com。此时网页会自动发送 Ajax 请求(此请求属于简单请求,浏览器端会正常发出,服务器端能够接受并响应,但浏览器端的 JavaScript 无法得到响应消息)。由于a.example.com的 cookie 已存在于浏览器中,且withCredentialstrue,故此时访问a.example.com/transferMoney会自动带上a.example.com的 cookie。而后用户在a.example.com的资金被盗。

根本矛盾

服务器端只能确认请求由用户的浏览器发出,但无法确认请求是否出自用户本意

防御措施

在表单中嵌入 token

原理:

  1. 在服务器端生成随机的 token,并将 token 保存在服务器端的 session 中,而后将其嵌入所有<form>
  2. 当服务器端收到请求时,校验表单里的 token 和服务器端的 token 是否一致

把 token 嵌入网络请求头

原理:

  1. 在服务器端生成随机的 token,并将 token 保存在服务器端的 session 中,同时将 token 告知客户端
  2. 客户端每次发起请求,必须把 token 嵌入 Ajax 的网络请求头
  3. 当服务器端收到请求时,校验网络请求头里的 token 和服务器端的 token 是否一致

SameSite

原理:阻止发送跨站 cookie

参考资料

本文作者: jsweibo

本文链接: https://jsweibo.github.io/2020/02/21/%E4%BB%80%E4%B9%88%E6%98%AFCSRF/


本文对你有帮助?请支持我


支付宝
微信