浏览器安全:34 | CSRF攻击:陌生链接不能随便点

前言:该篇说明:请见 说明 —— 浏览器工作原理与实践 目录

 

  上一篇文章中讲到了 XSS 攻击,XSS 的攻击方式是黑客往用户页面中注入恶意脚本,然后再通过恶意脚本将用户页面的数据上传到黑客服务器上,最后黑客再利用这些数据进行一些恶意操作。XSS 攻击能带来很大的破坏性,不过另外一种类型的攻击也不容忽视,它就是今天要讲的 CSRF 攻击。

  相信经常能听到这句话:“别点那个链接,小心有病毒!”,点击一个链接怎么就染上病毒?

  下面结合一个真实的关于 CSRF 攻击的典型案例来分析下,在 2007 年的某一天,David 无意间打开了 邮箱中的一份邮件,并点击了该邮件中的一个链接。过了几天,就发现他的域名被盗了。不过几经周折还是要回了他的域名,也弄清楚了域名之所以被盗,就是因为无意间点击的那个链接。

 

那域名是怎么被盗的?

  结合下图来分析下被盗的流程:

 域名被盗流程

  • 首先 David 发起登录邮箱请求,然后 Gmail 服务器返回一些登录状态给浏览器,这些信息包括了 Cookie、Session 等,这样在浏览器中,Gmail 邮箱就处于登录状态了。
  • 接着黑客通过各种手段引诱他去打开链接,比如hacker.com,然后在 hacker.com 页面中,黑客编写好了一个邮件过滤器,并通过 Gmail 提供的 HTTP 设置接口设置好了新的邮件过滤功能,该过滤器会将他所有的邮件都转发到黑客的邮箱中。
  • 最后的事情很简单了,因为有了 David 的邮件内容,所以黑客就可以去域名服务商那边重置David 域名账户的密码,重置好密码之后,就可以将其转出到黑客的账户了。

  以上就是David 的域名被盗的整个过程,其中前两步就是今天要讲的 CSRF 攻击。

 

什么是 CSRF 攻击

  CSRF 全称 Cross-site request forgery,所以又称为“跨站请求伪造”,是指黑客引诱用户打开黑客的网站,在黑客的网站中,利用用户的登录状态发起的跨站请求,简单来说,CSRF 攻击就是黑客利用了用户的登录状态,并通过第三方的站点来做一些坏事

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

  下面以极客时间官网为例子,来分析这三种攻击方式都是怎么实施的。假设极客时间具有转账功能,可以通过 POST 或 GET 来实现转账,转账接口如下:

#同时支持POST和Get
#接口 
https://time.geekbang.org/sendcoin
#参数
##目标用户
user
##目标金额
number

 

  有了上面的转账接口,就可以来模拟 CSRF 攻击了。

 

1. 自动发起 GET 请求

  黑客最容易实施的攻击方式是自动发起 GET 请求,具体攻击方式可以如下代码:

<!DOCTYPE html>
<html>
  <body>
    <h1>黑客的站点:CSRF攻击演示</h1>
    <img src="https://time.geekbang.org/sendcoin?user=hacker&number=100">
  </body>
</html>

  这是黑客页面的 HTML 代码,在这代码中,黑客将转账的请求接口隐藏在 img 标签中,欺骗浏览器这是一张图片资源。当该页面被加载时,浏览器会自动发起 img 的资源请求,如果服务器没有对该请求做判断的话,那么服务器就会认为该请求是一个转账请求,于是用户账户上的 100 极客币就被转移到黑客的账户上去了。

 

2. 自动发起 POST 请求

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

<!DOCTYPE html>
<html>
<body>
  <h1>黑客的站点:CSRF攻击演示</h1>
  <form id='hacker-form' action="https://time.geekbang.org/sendcoin" 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 width=150 src=http://images.xuejuzi.cn/1612/1_161230185104_1.jpg> </img> </div> <div>
  <a href="https://time.geekbang.org/sendcoin?user=hacker&number=100" taget="_blank">
    点击下载美女照片
  </a>
</div>

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

 

  以上三种方式就是黑客经常采用的攻击方式。如果当用户登录了极客时间,以上三种 CSRF 攻击方式中的任何一种发生时,那服务器都会将一定金额的极客币转到黑客账户。

 

  现在知道什么是 CSRF攻击了。和 XSS 不同的是,CSRF攻击不需要将恶意代码注入用户的页面,仅仅是利用服务器的漏洞和用户登录状态来实施攻击

 

如何防止 CSRF 攻击

  了解了 CSRF 攻击的手段后,再来看看 CSRF 攻击的 一些“特征”,然后根据这些“特征”分析下如何防止 CSRF 攻击。

  

  CSRF 攻击的三个必要条件:

  1、目标站点一定要有 CSRF 漏洞;

  2、用户要登录过目标站点,并且在浏览器上保持有该站点的登录状态;

  3、需要用户打开一个第三方站点,可以是黑客的站点,也可以是一些论坛。

 

  满足以上三点,黑客就可以对用户进行 CSRF 攻击了。这里还需要额外注意一点,与 XSS 攻击不同,CSRF 攻击不会往页面注入恶意脚本,因此黑客是无法通过 CSRF 攻击来获取用户页面数据;其最关键一点是要能找到服务器的漏洞,所以说对于 CSRF 攻击,主要的防护手段是提升服务器的安全性

  要让服务器避免遭受到 CSRF 攻击,通常有以下几种途径。

 

1. 充分利用好 Cookie 的 SameSite 属性

  现在知道黑客会利用用户的登录状态来发起 CSRF 攻击,而 Cookie 正是浏览器和服务器之间维护登录状态的一个关键数据,因此要阻止 CSRF 攻击,首先就要考虑在 Cookie 上做文章。

 

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

  • 如果是从第三方站点发起的请求,需要浏览器禁止发送某些关键 Cookie 数据到服务器;
  • 如果是同一个站点发起的请求,就需要保证 Cookie 数据正常发送。

  

  而 Cookie 的 SameSite  属性正是为了解决这个问题的,通过使用 SameSite 可以有效降低 CSRF 攻击的风险

  那 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 和 Node 三个值

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

  

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

 

2. 验证请求的来源站点

  再来了解另一种防止  CSRF 攻击的策略:在服务器端验证请求来源的站点。由于 CSRF 攻击大多来自第三方站点,因此服务器可以禁止来自第三方站点的请求。那该如何判断请求是否来自第三方站点?

 

 这就需要介绍 HTTP 请求头中的 Referer 和 Origin 属性了。

 

 Referer 是 HTTP 请求头中的一个字段,记录了该 HTTP 请求的来源地址。比如从极客时间官网打开了 InfoQ 的站点,那请求头中的 Referer 值是极客时间的 URL,如下图:

 HTTP 请求头中的 Referer 引用

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

 

  但在服务器端验证请求头中的 Referer 并不是太可靠,因此标准委员会又指定了 Origin 属性,在一些重要场合,比如通过 XMLHTTPRequest、Fetch 发起跨站点请求或通过 Post 方法发送请求时,都会带上 Origin 属性,如下:

 Post 请求时的 Origin 信息

  从上图可以看出,Origin 属性只包含了域名信息,并没有包含具体的 URL 路径,这是 Origin 和 Referer 的一个主要区别。在这里需要补充一点,Origin 的值之所以不包含详细路径信息,是有些站点因为安全考虑,不想把源站点的详细路径暴露给服务器。

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

 

3. CSRF Token

  除了使用以上两种方式防止 CSRF 攻击外,还可以采用 CSRF Token 来验证,这个流程比较好理解,大致分为两步:

  

  第一步:在浏览器向服务器发起请求时,服务器生成一个 CSRF Token。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 不正确而拒绝请求。

 

总结

  总结本文的主要内容:

  结合一个实际案例介绍了 CSRF 攻击,要发起 CSRF 攻击需要具备三个特征:目标站点存在漏洞、用户登录过目标站点和黑客需要通过第三方站点发起攻击。

 

  根据这三个必要条件,又介绍了该如何防止 CSRF 攻击,具体来讲主要有三种方式:充分利用 Cookie 的 SameSite 属性、验证请求的来源站点和使用 CSRF Token。这三种方式需要合理搭配使用,这样才能有效地防止 CSRF 攻击。

 

  再结合前面两篇文章,可以得出页面安全问题的主要原因是 浏览器为同源策略开的两个“后门”:一个是在页面中可以任意引用第三方资源,两外一个是通过 CORS 策略让 XMLHTTPRequest 和 Fetch 去跨域请求资源。

 

  为了解决这些问题,引入了 CSP 来限制页面任意引入外部资源,引入了 HttpOnly 机制来禁止 XMLHttpRequest 或 Fetch 发送一些关键 Cookie,引入了 SameSite 和 Origin 来防止 CSRF 攻击。

 

  通过这三篇文章的分析,应该能搭建 Web 页面安全的知识体系网络了。有了这张网络,就可以将 Http 请求头和响应头中各种安全相关的字段关联起来,比如 Cookie 中的一些字段,还有 X-Frame-Options、X-Content-Type-Options、X-XSS-Protection 等字段,也可以将 CSP、CORS 这些知识点关联起来。当然这些并不是浏览器安全的全部,后面两篇文章还会介绍浏览器系统安全浏览器网络安全两大块内容。

 

思考题

  什么是 CSRF 攻击?在开发项目过程中应该如何防御 CSRF 攻击?

个人总结:

CSRF 攻击:

  用户登录网1,网1 中存在诱导用户点击的黑客链接,用户点击链接进入第三方站点,又第三方站点(黑客站点)利用当前用户的登录状态发起请求(do something)。

如何防御:三点

  1. set-cookie 中的 SameSite

  2.服务器判断请求来源,Origin / Referer

  3. CSRF Token 验证

 

记录

1、“简言之,如果你从极客时间的页面中访问 InfoQ 的资源,而 InfoQ 的某些 Cookie 设置了 SameSite = Strict 的话,那么这些 Cookie 是不会被发送到 InfoQ 的服务器上的”,这里是不是我理解错了还是写错了。应该是不会发送到极客时间的服务器上,或者说极客时间的某些Cookie设置了SameSite = Strict吧。

作者回复:我把整个流程写一遍:

首先假设你发出登录InfoQ的站点请求,然后在InfoQ返回HTTP响应头给浏览器,InfoQ响应头中的某些set-cookie字段如下所示:

set-cookie: a_value=avalue_xxx; expires=Thu, 21-Nov-2019 03:53:16 GMT; path=/; domain=.infoq.com; SameSite=strict

set-cookie: b_value=bvalue_xxx; expires=Thu, 21-Nov-2019 03:53:16 GMT; path=/; domain=.infoq.com; SameSite=lax

set-cookie: c_value=cvaule_xxx; expires=Thu, 21-Nov-2019 03:53:16 GMT; path=/; domain=.infoq.com; SameSite=none

set-cookie: d_value=dvaule_xxxx; expires=Thu, 21-Nov-2019 03:53:16 GMT; path=/; domain=.infoq.com;

 

可以看出,

a_value的SameSite属性设置成了strict,

b_value的SameSite属性设置成了lax

c_value的SameSite属性值设置成了none

d_value没有设置SameSite属性值

 

好,这些Cookie设置好之后,当你再次在InfoQ的页面内部请求InfoQ的资源时,这些Cookie信息都会被附加到HTTP的请求头中,如下所示:

cookie: a_value=avalue_xxx;b_value=bvalue_xxx;c_value=cvaule_xxx;d_value=dvaule_xxxx;

 

但是,假如你从time.geekbang.org的页面中,通过a标签打开页面,如下所示:

<a href="https://www.infoq.cn/sendcoin?user=hacker&number=100">点我下载</a>

当用户点击整个链接的时候,因为InfoQ中a_vaule的SameSite的值设置成了strict,那么a_vaule的值将不会被携带到这个请求的HTTP头中。

 

如果time.geekbang.org的页面中,有通过img来加载的infoq的资源代码,如下所示:

<img src="https://www.infoq.cn/sendcoin?user=hacker&number=100">

那么在加载infoQ资源的时候,只会携带c_value,和d_value的值。

 

2、老师,我想请问一下,在浏览器打开第三方站点是如何拿到极客时间站点cookie的?第三方站点和极客时间的站点不同,存在同源策略,所以转账请求验证cookie也是不通过的,那么CSRF是如何攻击的呢?

层主的问题梳理一下是两个点:1.第三方网站是如何拿到cookie的 2.同源策略为什么不能阻止CSRF。 同源策略并没有完全限制网站不能使用非同源的资源,比如引用第三方script文件,引用第三方CSS文件等,同样的,也没有限制一些跨域写操作,比如表单提交。因此,光靠同源策略不能阻止CSRF。明白了可以在第三方站点成功发送跨域请求这一点之后,浏览器会自动带上请求的那个站点的cookie。

 

3、有个疑问:
same origin policy不是确保了不同域名时间不可以访问数据的吗? 那第三方站点如何拿到cookie和session?

作者回复:

  如果是CSRF攻击,那么黑客是拿不到受害者站点数据的。

  但是黑客会在他的A站点中调用受害者B站点的http接口,这些接口可以是转账,删帖或者设置等。

  这个过程中你需要注意一点,在黑客A站点中调用受害者B站点的http接口时,默认情况下,浏览器依然会把受害者的Cookie等信息数据发送到受害者的B站点,【注意这里并不是黑客的A站点】。

  如果B站点存在漏洞的话,那么黑客就会攻击成功,比如将受害者的金币转出去了!

 

4、CSRF TOKEN类似于cookie, 都是存储用户信息,而此用户信息只存储在当前你请求的站点,而不是浏览器,所以不同的标签页面,或不同次的请求是不共享此用户信息的,所以规避了cookie所带来的的漏洞,老师,我这样理解对?

作者回复:

  是的,针对于请求的,比如同一个浏览器打开两个相同页面,那么服务器为这两个页面生成的csrf token都是不同的

posted on 2022-03-11 15:45  bala001  阅读(601)  评论(0编辑  收藏  举报

导航