【前端安全】关于 XSS 和 CSRF

695 阅读6分钟

随着技术的发展,网站的安全性变得越来越重要,不单单是运维 后端要懂网络安全,前端也要略懂一点,所以我们来简单了解一下前端的一些安全的相关的知识。

同源策略

我们知道,如果两个 url 的 协议 域名 端口,都相同,那么我们可以认为他们是 同源 的,反之只要有一个不一样,浏览器就会认为他们是 非同源

这是浏览器的一个安全限制,不同源的客户端在网站没有明确的授权允许访问的情况下,不能读取资源。

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

XSS

XSS - 跨网站脚本攻击,全称是 Cross Site Scripting (缩写其实应该是 CSS ,但是这样就和样式的缩写冲突了,所以叫 XSS)

XSS 是现在前端网站危害范围非常广的攻击方式。

我们可以从两个维度来区分不同的 XSS 攻击方式:

  • 从攻击时限分为:持久型 / 非持久型

  • 从攻击方式上分为:反射型 / 存储型 / DOM—XSS (由于 DOM—XSS 和反射型很相似,所以可以统称为非持久型攻击)

反射型 XSS 攻击 —— 非持久型

反射型,字面意思上其实很好理解,比如说:我向你发起了一个攻击,你受到攻击后,又把伤害反射给我了。

所以反射型 XSS 攻击就是: 用户将一段含有恶意代码的请求提交给 Web 服务器,Web 服务器接收到请求时,又将恶意代码反射给了浏览器端,这就是反射型 XSS 攻击。 在现实生活中,黑客经常会通过 QQ 群或者邮件等渠道诱导用户去点击这些恶意链接。

攻击方向:浏览器(URL) -> 后台 -> 单个用户

攻击方向是输入URL,后台接受攻击代码并把代码响应回了前端,这也就意味着,该攻击只对当前打开攻击链接的用户有效,页面关闭,攻击的影响就不存在了,所以是非持久的。

存储型 XSS 攻击 —— 持久型

利用漏洞提交恶意的JavaScript代码,比如在input和textare区域内填写一段脚本代码,当用户存储然后再打开预览页面的时候,该段脚本就会执行,然后将用户相关的信息(如cookie等)上传到对方服务器。

攻击方向:浏览器 -> 后台 -> 数据库 -> 后台 -> 用户(所有使用者)

持久存储型 XSS 攻击,该攻击将攻击代码存储到数据库,页面关闭,如果没有任何安全措施,那么该系统所有的使用者,只要进入到攻击页面,都会受到攻击,数据不消失,攻击就是永久有效的。

一个最简单的例子,就是表单存储了。比如,一个博客系统,攻击者输入恶意评论,那么该文章只要被展示,该评论就会被渲染攻击正在访问的用户。为了进行简单的代码演示,这里我的 Demo 写的比较粗糙,毕竟不是所有场景都适合假想代码的,很多都必须实际应用中才行。

预防策略

  1. 将 cookie 等敏感信息设置为 httpOnly,禁止 Javascript 通过 document.cookie 获得。

  2. 对所有的输入做严格的校验尤其是在服务器端,过滤掉任何不合法的输入,比如手机号必须是数字。

  3. 净化和过滤掉不必要的 html 标签,比如: <iframe>, alt, <script> 净化和过滤掉不必要的 Javascript 的事件标签,比如:onclick, onfocus等。

  4. 转义单引号,双引号,尖括号等特殊字符,可以采用 html encode 编码或者过滤掉这些特殊字符。

所以一般来说前后端都要做校验,前端校验的是输入合法性,后端校验的是安全性相关,上面也提到了,事实上,前端做不做都不会有什么影响,恶意脚本代码并不会执行,这个事应该是后端去做的。

CSRF

CSRF - 跨站请求伪造(CSRF - Cross Site Request Forgery)

引诱用户打开黑客的网站,在黑客的网站中,利用用户的登录状态发起的跨站请求。 发起 CSRF 攻击的三个必要条件:

  1. 目标站点一定要有 CSRF 漏洞;
  2. 用户要登录过目标站点,并且在浏览器上保持有该站点的登录状态;
  3. 需要用户打开一个第三方站点,如黑客的站点等。

预防策略

  1. 验证请求的来源 - 如果是敏感请求,可以判断一下请求的Origin和Referer。Referer 是 HTTP 请求头中的一个字段,记录了该 HTTP 请求的来源地址,而O rigin 属性只包含了域名信息,并没有包含具体的 URL 路径。这是 Origin 和 Referer 的一个主要区别。服务器的策略是优先判断 Origin,如果请求头中没有包含 Origin 属性,再根据实际情况判断是否使用 Referer 值。
  2. 使用token验证 - 所有请求的身份信息判断使用 token 来验证

类似的安全相关场景

  1. 点击劫持 - 解决方法:使用验证码

    • 诱使用户点击看似无害的按钮(实则点击了透明 iframe 中的按钮)
    • 监听鼠标移动事件,让危险按钮始终在鼠标下方
    • 使用 HTML5 拖拽技术执行敏感操作(例如 deploy key).
  2. window.opener 安全问题

    window.opener 表示打开当前窗体页面的的父窗体的是谁。例如,在 A 页面中,通过一个带有 target="_blank" 的 a 标签打开了一个新的页面 B,那么在 B 页面里,window.opener 的值为 A 页面的 window 对象。 一般来说,打开同源(域名相同)的页面,不会有什么问题。但对于跨域的外部链接来说,存在一个被钓鱼的风险。比如你正在浏览购物网站,从当前网页打开了某个外部链接,在打开的外部页面,可以通过 window.opener.location 改写来源站点的地址。利用这一点,将来源站点改写到钓鱼站点页面上,例如跳转到伪造的高仿购物页面,当再回到购物页面的时候,是很难发现购物网站的地址已经被修改了的,这个时候你的账号就存在被钓鱼的可能了

解决方式 :

  • 设置 rel 属性,<a href="https://xxxx" rel="noopener noreferrer"> 外链 <a>, rel="noopener noreferrer" 规定禁止新页面传递源页面的地址,通过设置了此属性的链接打开的页面,其 window.opener 的值为 null。
  • 可以由 widow.open 打开外链。

总结

安全问题发生在前端(无论是 url 还是 form 表单),而处理办法则是在后台。