CSRF 跨站请求伪造
常见的浏览器攻击分为两种,一种为 XSS(跨站脚本攻击),另一种则为 CSRF(跨站请求伪造)
一、CSRF 的定义
CSRF(Cross-site request forgery)即跨站请求伪造,它是利用用户已登录的身份,在用户不知情的情况下,以用户的名义完成非法操作的手段。
CSRF 攻击不需要将恶意代码注入用户的页面,仅仅是利用服务器的漏洞和用户的登录状态来实施攻击。
CSRF 的原理:
- 用户已经登录了站点 A,并在本地记录了 Cookie;
- 在用户没有登出站点 A 的情况下(也就是 Cookie 生效的情况下),访问了攻击者提供的危险站点 B;
- 站点 A 没有做任何 CSRF 防御。
二、CSRF 存在原因
利用了 Web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
三、CSRF 攻击方式
1、自动发起 GET 请求
黑客最容易实施的攻击方式是自动发起 GET 请求:
<!DOCTYPE html>
<html>
<body>
<h1>黑客的站点</h1>
<img src="xxx">
</body>
</html>
这是黑客页面的 HTML 代码,在这段代码中,黑客将转账的请求接口隐藏在 img 标签内,欺骗浏览器这是一张图片资源。当该页面被加载时,浏览器会自动发起 img 的资源请求,如果服务器没有对该请求做判断的话,那么服务器就会认为该请求是一个转账请求,于是用户账户上的钱就被转到黑客账户上去了。
2、自动发起 POST 请求
除了自动发送 GET 请求之外,有些服务器的接口是使用 POST 方法的,所以黑客还需要在他的站点上伪造 POST 请求,当用户打开黑客的站点时,是自动提交 POST 请求:
<!DOCTYPE html>
<html>
<body>
<h1>黑客的站点</h1>
<form id="hacker-form" action="xxx" method=POST>
<input type="hidden" name="user" value="hacker" />
<input type="hidden" name="number" value="100" />
</form>
<script>
document.getElementById("hacker-form").submit();
</script>
</body>
</html>
在这段代码中,可以看到黑客在他的页面中构建了一个隐藏的表单,该表单的内容就是极客时间的转账接口。当用户打开该站点之后,这个表单会被自动执行提交;当表单被提交之后,服务器就会执行转账操作。因此使用构建自动提交表单这种方式,就可以自动实现跨站点 POST 数据提交。
3、引诱用户点击链接
除了自动发起 GET 和 POST 请求之外,还有一种方式是诱惑用户点击黑客站点上的链接,这种方式通常出现在论坛或者恶意邮件上。黑客会采用很多方式去诱惑用户点击链接,示例代码如下所示:
<div>
<img src="xxx">
<a href="xxx" target="_blank">
快来点我吧
</a>
</div>
这段黑客站点代码,页面上放了一张引诱点击的图片与图片下载地址,而这个下载地址实际上是黑客用来转账的接口,一旦用户点击了这个链接,那钱就被转到黑客账户上了。
和 XSS 不同的是,CSRF 攻击不需要将恶意代码注入用户的页面,仅利用服务器的漏洞和用户的登录状态来实施攻击。
四、CSRF 的危害
利用登陆态,可以做任何你登陆某网站后的操作,例如转账,更改信息等等。
五、如何防御 CSRF
1、对 Cookie 设置 SameSite 属性
Cookie 的 SameSite 属性用来限制第三方 Cookie,从而减少安全风险。可以设置三个值:
Strict:浏览器会完全禁止第三方 Cookie,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
Lax:在跨站点的情况下 Lax 规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 GET 请求除外。包括链接,预加载请求,GET 表单。而如果在第三方站点中使用 POST 方法,或者通过 img、iframe 等标签加载的 URL,这些场景都不会携带 Cookie。
None:在任何情况下都会发送 Cookie 数据,最新版本的 Chrome 中 Lax 变为默认设置。
2、根据 Referer 验证请求的来源站点
Referer 是 HTTP 请求头中的一个字段,记录了该 HTTP 请求的来源地址。
虽然可以通过 Referer 告诉服务器 HTTP 请求的来源,但是有些场景是不适合将来源 URL 暴露给服务器的,因此标准委员会又制定了Origin 属性:
Origin 属性只包含了域名信息,并没有包含具体的 URL 路径。
服务器的策略是优先判断 Origin,如果请求头中没有包含 Origin 属性,再根据实际情况判断是否使用 Referer 值。
3、加 CSRF Token 验证
除了使用以上两种方式来防止 CSRF 攻击之外,还可以采用 CSRF Token 来验证,这个流程大致分为两步:
<!DOCTYPE html>
<html>
<body>
<form action="xxx" method=POST>
<input type="hidden" name="csrf-token" value="nda5s7adj1231jl" />
<input type="text" name="user" />
<input type="text" name="number" />
<input type="submit" />
</form>
</body>
</html>
- 在浏览器向服务器发起请求时,服务器生成一个 CSRF Token。CSRF Token 其实就是服务器生成的字符串,然后将该字符串植入到返回的页面中;
- 在浏览器端如果要发起某些请求,那么需要带上页面中的 CSRF Token,然后服务器会验证该 Token 是否合法。如果是从第三方站点发出的请求,那么将无法获取到 CSRF Token 的值,所以即使发出了请求,服务器也会因为 CSRF Token 不正确而拒绝请求。