Web安全之点击劫持、XSS、CSRF

1,240 阅读4分钟

本文描述了各种类型的安全攻击以及减轻这些攻击。

点击劫持(Clickjacking)

表现

“点击劫持”攻击允许恶意页面 以用户的名义 点击“受害网站”。

实现

  1. 用户登录微博后,weibo.comcookies里包含登录信息。
  2. 用户访问恶意页面(可能是点击了广告或者其他方式);
  3. 恶意页面中嵌入了一个透明的 <iframe>,其 src 来自于 weibo.com
  4. 页面勾引用户点击了一个<button>,实际上用户点击的是覆盖来自<iframe>中的微博的点赞按钮👍;

防御

使用 Samesite cookie 特性

具有 SameSite 特性的cookie仅在网站是通过直接方式打开(而不是通过 iframe 或其他方式)的情况下才发送到网站。如果网站在 cookie 中具有 samesite 特性,像这样:Set-Cookie: authorization=secret; sameSite=Lax,当在另一个网站中的 iframe 中打开微博时,此类 cookie 将不会被发送,微博登录失败,攻击失败。

XSS(Cross-Site Scripts)

跨站脚本攻击:因为 CSS 容易和样式表缩写混淆,所以简称 XSS;

表现

攻击者将恶意 JS 代码注入网站。

来源

  1. 来自用户生成的信息,后端未做清洗,直接进行拼接进 HTML 中,返回给客户端;
  2. 来自URL:前端或者后端从 url queryurl hash 中直接读取参数拼接进 HTML 中。

分类

XSS 攻击分为三类:存储(也称为持久)、反射(也称为非持久)和基于 DOM。

  1. 存储型 XSS 攻击

    1. 攻击代码来源于用户输入,比如评论、留言功能。
    2. 注入的脚本永久存储在目标服务器上。然后,当浏览器发送数据请求时,受害者会从服务器检索此恶意脚本。
  2. 反射型 XSS 攻击

    1. 攻击代码来源于 URL
    2. 用户打开带有恶意代码的 URL 时,服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. 基于 DOM 的 XSS 攻击

    1. 攻击代码来源于 URL
    2. 用户打开带有恶意代码的 URL 时,前端 JavaScript 取出 URL 中的恶意代码并执行。
    3. 获取和执行恶意代码都由浏览器完成,属于前端 JS 代码自身的安全漏洞。

防御

  • 改成纯前端渲染,把代码和数据分隔开;
  • 对 HTML 做充分转义;
  • 不要把不可信的数据拼接到字符串中传递给 JS 执行;
  • 使用 CSP 安全策略;
  • 使用 Samesite CookiesHttpOnly亡羊补牢,即使被注入恶意代码,攻击者也拿不到Cookie

CSRF(Cross-Site Request Forgery)

表现

  1. 用户登录银行网站bank.combank.com下的cookies存储了用户登录token;
  2. 用户访问了恶意网站evil.comevil.com在网页中设置了表单
  3. evil.combank.com发送了一个请求:bank.com?payfor=22000&to=xoyimi。浏览器会默认携带bank.comcookies
  4. bank.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
  5. bank.com以受害者的名义执行了payfor=22000&to=xoyimi

途径

通过图片URL、超链接、CORS、Form提交等等一切可以发送请求的地方。

防御

  1. 验证 HTTP 的RefererOrigin请求头;
  2. 在 HTTP 头中自定义属性并验证或者使用 CSRF-Token 技术(因为第三方网站只会发送 Cookie,不会发送自定义 header);
  3. Cookie中设置sameSite=[Lax|Strict],Chrome 51 及以上可以开启此功能;
  4. 不在 GET 请求中做增删改操作,sameSite=Lax 表单 GET 请求默认会发送 Cookie

tips: 方案1,2要防止黑客配合XSS攻击获取同源脚本执行权限;

这里着重介绍一下 SameSite Cookies

CSRF 漏洞的末日?关于 Cookie SameSite 那些你不得不知道的事 - 知乎 (zhihu.com)

Chrome 80 之前默认为SameSite=None
Chrome 80 及以上默认开启 SameSite=Lax
如单独写入SameSite则等同于SameSite=Strict
值得注意的是,火狐Safari 并没有默认开启 SameSite=Lax,仍需要手动设置。

跨站请求类型示例NoneLaxStrict
页面跳转<a href="..."></a>发送 Cookie发送 Cookie不发送
页面跳转window.open()发送 Cookie发送 Cookie不发送
页面跳转修改 location发送 Cookie发送 Cookie不发送
页面跳转<form method="GET" action="...">发送 Cookie发送 Cookie不发送
页面跳转<form method="POST" action="...">发送 Cookie不发送不发送
预加载(预跳转)<link rel="prerender" href="..."/>发送 Cookie发送 Cookie不发送
页内跨站请求<iframe src="...">发送 Cookie不发送不发送
页内跨站请求<img src="...">发送 Cookie不发送不发送
页内跨站请求ajax (withCredentials:true)发送 Cookie不发送不发送
页内跨站请求fetch (credentials:include)发送 Cookie不发送不发送

值得注意的一点是:即便ajax/fetch声明携带credentials,但在SameSite=[Lax/Strict]下也不会发送Cookie.

和 CSRF 嗦拜拜~

以上皆在跨站的情况下。

引用文献:

  1. CSRF 漏洞的末日?关于 Cookie SameSite 那些你不得不知道的事 - 知乎 (zhihu.com)
  2. developers.google.com/search/blog…
  3. SameSite Updates (chromium.org)
  4. "SameSite" | Can I use... Support tables for HTML5, CSS3, etc