web网站安全 CSRF介绍及解决方案

1. 什么是CSRF?

CSRF 英文全称是 Cross-site request forgery,所以又称为“跨站请求伪造”。

是指恶意的网站通过脚本向当前用户浏览器打开的其它页面的 URL 发起恶意请求,由于同一浏览器进程下 Cookie 可见性,导致

用户身份被盗用,完成恶意网站脚本中指定的操作。简单来讲,CSRF就是黑客利用了用户的登录状态,并通过第三方的站

点来做一些坏事。

下面我们使用转账接口模拟CSRF

通常当用户打开了黑客的页面后,黑客有三种方式去实施 CSRF。

下面我们使用一个转账的例子。

比如转账操作:

  1. 输入: 127.0.0.1:9000/transfer?to_user=jack&money=100

    提示:请登录后操作

    然后进行登录

  2. 登录成功后再次进行输入: 输入: 127.0.0.1:9000/transfer?to_user=jack&money=100, 然后提示转账成功

  3. 然后我们访问另外一个网站: 127.0.0.1:5000

1.1 自动发起GET请求

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<h1>这是黑客站点: CSRF演示</h1>
		<img src="http://127.0.0.1:9000/transfer?to_user=jack&money=2000" />

	</body>
</html>

上面黑客页面的HTML代码,在这段代码中,黑客转账的请求隐匿在img标签内,欺骗浏览器这时一张图片。当该页面被加载的时候,浏览器会自动加载img,也就是自动发送请求进行转账,于是用户账户的200元就被转移到黑客的账户上了。

1.2 自动发起POST请求

有些服务器的接口是使用POST方法,所以黑客还需要在他的站点上伪造POST请求,当用户打开黑客的站点时,是自动提交POST请求,具体代码如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<h1>这是黑客站点: CSRF演示</h1>
		<form method="POST" action="http://127.0.0.1:9000/transfer" id="csrf-form">
			<input type="hidden" name="to_user" value="hick01"/>
			<input type="hidden" name="money" value="1000"/>
		</form>
		<script>document.getElementById("csrf-form").submit()</script>

	</body>
</html>

上面代码中,黑客在他的页面上构建了一个隐藏的表单,该表单的内容就是转账接口。当用户打开该站点后,这个表单就会被自动执行;当表单被提交之后,服务器就会执行转账操作。因此使用构建自动提交表单这种方式,就可以自动实现跨站POST数据提交。

那么为什么会出现CSRF 漏洞呢? 是因为我们自己的服务器验证不够。
在这里插入图片描述

1.3 引诱用户点击链接

黑客通常采取很多方式去诱惑用户点击链接。示例代码如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<h1>这是黑客站点: CSRF演示</h1>
		<a href="http://127.0.0.1:9000/transfer?to_user=jack&money=2000">
			美女图片
		</a>

	</body>
</html>

这段黑客站点代码,页面上放了一张美女图片,下面放了图片下载地址,而这个下载地址实际上是黑客用来转账的接口,一旦用户点击了这个链接,那么他的极客币就被转到黑客账户上了。

  • XSS利用站点内的信任用户; 而 CSRF 则通过伪装成受信任用户的请求来利用受信任的网站
  • 与XSS相比,CSRF往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性

2 漏洞检测

  1. 检测CSRF漏洞是一项比较繁琐的工作,最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞
  2. 随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具:CSRFTesterCSRF Request Builder
  3. CSRFTester为例:
  • 首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息
  • 然后通过在CSRFTester中修改相应的表单等信息重新提交,这相当于一次伪造客户端请求
  • 如果修改后的测试请求被网站服务器接收,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF。

3 解决方案

3.1 验证请求的来源

由于CSRF大多来源于第三方网站,因此服务器可以禁止来自第三方站点的请求。那么该怎么判断请求是否来自第三方站点呢?

那就要用到HTTP请求头中的 Referer 和 Origin 属性。

Refer 是HTTP请求头中的的一个字段,记录了该HTTP请求的来源地址。,比如我们打开CSDN网站,那么请求头中的Referer值就是CSDN的URL。

在这里插入图片描述
这时后端只需要加一个拦截器来检查Referer值就行,特别是现有的系统,不需要改变当前系统的任何已有代码和逻辑。

虽然可以通过 Referer 告诉服务器 HTTP 请求的来源,但是有一些场景是不适合将来源 URL 暴露给服务器的,因此浏览器提供给开发者一个选项,可以不用上传 Referer 值,具体可参考Referrer Policy

但在服务器端验证请求头中的 Referer 并不是太可靠,因此标准委员会又制定了Origin 属性,在一些重要的场合,比如通过 XMLHttpRequest、

Fecth 发起跨站请求或者通过 Post 方法发送请求时,都会带上 Origin 属性,如下图:
在这里插入图片描述
从上图可以看出,Origin 属性只包含了域名信息,并没有包含具体的 URL 路径,这是 Origin 和 Referer 的一个主要区别。在这里需要补充一点,Origin 的值之所以不包含详细路径信息,是有些站点因为安全考虑,不想把源站点的详细路径暴露给服务器。

因此,服务器的策略是优先判断 Origin,如果请求头中没有包含 Origin 属性,再根据实际情况判断是否使用 Referer 值。

3.2 CSRF Token

我们还可以采用 CSRF Token来验证,主要分为两步:

第一步,当用户端我们可以为每个用户生成不同的token,保存到sessionzhong 在浏览器向服务器发起请求时,服务器生成一个 CSRF Token。同时将此串token存入session。CSRF Token 其实就是服务器生成的字符串,然后将该字符串植入到返回的页面中。你可以参考下面示例代码:

<!DOCTYPE html>
<html>
<body>
    <form action="https://time.geekbang.org/sendcoin" method="POST">
      <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn">
      <input type="text" name="user">
      <input type="text" name="number">
      <input type="submit">
    </form>
</body>
</html>

第二步,在浏览器端如果要发起转账的请求,那么需要带上页面中的 CSRF Token,然后服务器会验证该 Token 是否合法。如果是从第三方站点发出的请求,那么将无法获取到 CSRF Token 的值,所以即使发出了请求,服务器也会因为 CSRF Token 不正确而拒绝请求。

3.3 在HTTP头中自定义属性并验证

自定义属性的方法也是使用token并进行验证,和前一种方法不同的是,这里并不是把token以参数的形式置于HTTP请求之中,而是把它放到HTTP头中自定义的属性里。通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过Referer泄露到其他网站。

我们就是采用的这种方法,因为基本上所有的请求都是通过ajax,我们通过重新封装ajax,在ajax头部添加一个token,server去识别这个token,如果请求没有这个token或者错误就拒绝。

3.4 Cookie 的SameSite属性

通常 CSRF都是从第三方站点发起的,要防止 CSRF,我们最好能实现从第三方站点发送请求时禁止 Cookie 的发送,因此在浏览器通过不同来源发送 HTTP 请求时,有如下区别:

  • 如果是从第三方站点发起的请求,那么需要浏览器禁止发送某些关键 Cookie 数据到服务器;

  • 如果是同一个站点发起的请求,那么就需要保证 Cookie 数据正常发送。

那 SameSite 是怎么防止 CSRF的呢?

在 HTTP 响应头中,通过 set-cookie 字段设置 Cookie 时,可以带上 SameSite 选项,如下:

set-cookie: 1P_JAR=2019-10-20-06; expires=Tue, 19-Nov-2019 06:36:21 GMT; path=/; domain=.google.com; SameSite=none

SameSite 选项通常有 Strict、Lax 和 None 三个值。

  • Strict 最为严格。如果 SameSite 的值是 Strict,那么浏览器会完全禁止第三方 Cookie。简言之,如果你从CSDN的页面中访问 InfoQ 的 资源,而 InfoQ 的某些 Cookie 设置了 SameSite = Strict 的话,那么这些 Cookie 是不会被发送到 InfoQ 的服务器上的。只有你从 InfoQ 的站点去请求 InfoQ 的资源时,才会带上这些 Cookie。

  • Lax 相对宽松一点。在跨站点的情况下,从第三方站点的链接打开和从第三方站点提交 Get 方式的表单这两种方式都会携带 Cookie。但如

    果在第三方站点中使用 Post 方法,或者通过 img、iframe 等标签加载的 URL,这些场景都不会携带 Cookie。而如果使用 None 的话,在任何情况下都会发送 Cookie 数据。

关于 SameSite 的具体使用方式,你可以参考这个链接:https://web.dev/samesite-cookies-explained 。

对于防范 CSRF,我们可以针对实际情况将一些关键的 Cookie 设置为 Strict 或者 Lax 模式,这样在跨站点请求时,这些关键的 Cookie 就不会被发送到服务器,从而使得黑客的 CSRF失效。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半夏_2021

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值