前端安全

130 阅读9分钟

XSS攻击

概念

XSS(Cross Site Scritpting)即跨站脚本攻击,是一种代码注入攻击,为了和CSS区分所以叫XSS。攻击者通过在目标往网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可以获取用户的敏感信息如cookie,sessionID等,进而危害数据安全。

本质

恶意代码未经过过滤,与网站正常代码混在一起,浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。由于直接在用户终端执行,恶意代码能够直接获取用户信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。同时攻击者可以通过引入外部脚本来完成比较复杂的攻击策略。

XSS注入方法

  1. 在HTML中内嵌的文本中,恶意内容以script标签形成注入
  2. 内联javascript中,拼接的数据突破了原本的限制(字符串,变量,方法名等)
  3. 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签
  4. 在标签的 href、src等属性中,包含javascript;等可执行代码
  5. onloadonerroronclick等事件中,注入不受控制代码

XSS攻击分类

存储型

恶意代码被保存到目标网站的数据库中,这种攻击具有较强的稳定性和持久性,用户打开目标网站时,网站服务端将恶意代码从数据库中取出,拼接在HTML中返回给浏览器。浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。恶意代码窃取用户数据并发送到攻击者网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。常见的场景比如论坛发帖、商品评论,用户私信等。

反射型

通过引诱用户点击一个链接到目标网站的恶意链接来实施攻击。攻击者构造出特殊的URL,其中包含恶意代码,用户打开带有恶意代码的URL时网站服务器将恶意代码从URL中取出,拼接在HTML中返回给浏览器。用户浏览器接收响应后解析执行,混在其中的恶意代码也被执行。

DOM型

攻击者构造出特殊的URL,其中包含恶意代码。用户打开代有恶意代码的URL,用户浏览器接收到响应后解析执行,前端javascritp取出URL中恶意代码并执行。 DOM型XSS与前两种XSS的区别:DOM型XSS攻击中,取出和执行恶意代码由浏览器端完成,属于前端javascript自身的安全漏洞,而其他两种XSS都属于服务端的安全漏洞。

XSS攻击的防御

XSS攻击主要有两大要素:
1. 攻击者提交恶意代码
2. 浏览器执行恶意代码

预防攻击者提交恶意代码(输入过滤)

  1. 在用户提交时由前端过滤输入,然后提交到后端
    不可行!因为以但攻击者绕过前端过滤直接构造请求,就可以提交恶意代码了

  2. 后端在写入数据库千对输入进行过滤,然后把“安全的”内容返回给前端。
    举个例子:1 < 2 首先我们并不确定内容要输出到哪里。

    1. 用户的输入内容可能同时提供给前端和客户端,而一旦经过了escapeHTML(),客户端显示的内容就会成为乱码(1 &lt; 7
    2. 在前端,不同位置所需编码也不同。当作为HTML拼接页面时可以正常显示,但是当通过AJAX返回然后赋值给javascript变量时前端得到的就是转义后的字符。 输入过滤能够在某些情况下解决特定的XSS问题,但是会引入不确定性和乱码问题。在防范XSS攻击时要避免采用这种方法。但是对于明确的输入类型,例如:数字、URL、电话号码、邮件地址等内容进行输入过滤还是很有必要的

预防浏览器执行恶意代码

  • 防止HTML中出现注入
  • 防止Javascript执行时执行恶意代码
预防存储型和反射型XSS攻击
  • 改成纯前端渲染,把代码和数据分隔开(现代前端领域常见的单页应用就是如此)
  • 对HTML做充分转义。(开启并利用模板引擎的转移功能,常见的如ejs模板等)
预防DOM型XSS攻击

需要前端自身在书写代码时提高意识。在使用.innerHTMLdocument.write()时要特别小心,不要把不可信的数据作为html插到页面上,而应尽量使用.textContentsetAtteibute()等。DOM中的内联事件监听器,如location、onclick、onerror、onload、onmouseover等,<a>标签的href属性,javascript的evval()、setTimeout()、setInterval()等都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些API,很容易产生安全隐患。

其他防范措施

  1. CSP内容安全策略(Content Security Policy)
  • 禁止加载外语代码,防止复杂的攻击逻辑
  • 禁止外域提交,网站被攻击后用户的数据不会泄露到外域
  • 禁止内联脚本执行(规则较严格,目前发现GitHub使用)
  • 禁止未授权的脚本执行(新特性,Google Map移动版在使用)
  • 合适使用上报可以及时发现XSS,有利于尽快修复问题
  1. 输入内容长度控制:对于不受信任的输入都应该限定一个合理的长度,这样可以增加XSS攻击的难度。
  2. Http-only Cookie:禁止 JavaScript 读取某些敏感 Cookie ,攻击者完成XSS攻击后也无法窃取 Cookie。
  3. 验证码:防止脚本冒充用户体提交危险操作

防范存储型和反身型xss攻击时后端RD的责任,而DOM型XSS攻击不发生在后端是前端RD的责任。转义应该在输出HTML时进行而不是在提交用户输入时进行。 不同的上下文,如:HTML属性,HTML文字内容,HTML注释,跳转链接,内敛JavaScript字符串,内敛CSS样式表,所需要转义的规则不一致。业务RD需要选取合适的转义库,并针对不同的上下文调用不同的转义规则。

参考文章:
tech.meituan.com/2018/09/27/… segmentfault.com/a/119000000…

CSRF攻击

概念

CSRF(Cross-site request forgery)跨站请求伪造,指黑客诱导用户点击链接打开黑客网站,然后黑客利用用户目前的登录状态发期跨站请求。和XSS攻击对比,CSRF攻击并不需要将恶意代码注入用户当前页面的HTML文档中,而是跳转到新的页面,利用服务器的验证漏洞用户之前的登录状态来模拟用户进行操作。

攻击原理

image.png 点击链接之后

  1. 自动发 GET 请求 黑客网站中可能有这样一段代码 <img src="https://xxx.com/info?user=hhh&count=100"> 进入页面后会自动发起一个get请求,这个请求会自动带上关于xxx.com的cookie信息(如果你已经在xxx.com中登陆过)
    假如服务器端没有相应的验证机制,它可能认为发请求的是一个正常的用户,因为携带了相应的cookie,然后进行相应的各种操作,可以是转账汇款以及其他的恶意操作。

  2. 自动发起 POST 请求 黑客可能自己填了一个表单,写了一段自动提交的脚本。

form id='hacker-form' action="https://xxx.com/info" method="POST">

<input type="hidden" name="user" value="hhh" />

<input type="hidden" name="count" value="100" />

</form>

<script>document.getElementById('hacker-form').submit();</script>

同样也会携带相应的用户 Cookie信息,让服务器误以为是一个正常的用户在操作,让各种恶意操作变为可能。

  1. 诱导点击发送 GET 请求 在黑客网站上可能会放一个链接,驱使你来点击 <a href="https://xxx/info?user=hhh&count=100" taget="_blank">点击进入修仙世界</a> 点击时候和自动发起 get 请求部分同理

防范措施

  1. 利用 Cookie 的 Samesite 属性
    CSRF攻击中重要的一个环节就是自动发送目标站点下 Cookie,然后就是这一份 Cookie 模拟了用户的身份,故在 cookie 上面下文章是防范的不二人选。 在 cookie 中有一个关键的字段Samesite可以对请求中的 cookie 的携带做一些限制。

    Strict: 浏览器完全禁止第三方请求携带 cookie,比如请求 sanyuan.com网站只能在sanyuan.com域名中的请求才能携带 cookie,其他网站请求都不能携带 cookie.
    Lax:只能在 get 方法提交表单或 a 标签发送 get 请求下可以携带 cookie
    None:即默认模式,请求会自动携带上 cookie

  2. 验证来源站点
    利用请求头中的两个字段: OriginReferer
    Origin只包含域名信息, Referer 包含具体的URL,但是这两者也都可以伪造,通过AJAX请求头即可,安全性略差

  3. CSRF Token
    首先浏览器向服务器发送请求,服务器生成一个字符串,将其植入到返回的页面中。然后浏览器如果要发送请求就必须带上这个字符串,然后服务器来验证是否合法,如果不合法就不响应,这个字符串就是 CSRF Token,通常第三方无法拿到这个token因此会被服务器给拒绝。

    在浏览器访问网站A时,网站A设置cookie会增加随机值csrf_token(也就是token)。

    对cookie不是太了解的同学请注意:CSRF是网站B通过盗用保存在客户端的cookie,以客户端的身份来非法访问网站A。而虽然token也是由后端放在cookie中的,但是需要前端在发送请求的时候来将这个token发给后端检验,从而达到防御的目的。

    Token就是令牌,最大的特点就是随机性,不可预测

    返回给浏览器时,cookie会储存在浏览器。然后前端提交表单或者非表单请求的时候,就将这个token获取到然后发送给后端,后端判断请求中的token和cookie中的token是否一致来判断是否为正常请求,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

    为什么放在cookie之后,别的网站获取不到cookie里面的token而自己的网站可以获取token中的cookie呢?

    因为cookie采取同源策略,只有相同域名的网页才能获取域名对应的cookie。
    表单请求

    <form method="post">
        <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
        <label>账户:</label><input type="text" name="to_account" placeholder="请输入对方账户"><br/>
        <label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/>
        <input type="submit" value="转账">
    </form>
    

    非表单请求: 在ajax获取数据时,添加 headers:{ 'X-CSRFToken':getCookie('csrf_token') }

    import axios from 'axios'
        axios.post('/user', {
        name: 'Lumiere'
      }, {
        headers:{
            'X-CSRFToken':getCookie('csrf_token')
        }
      })
      .then(function (response) {
        console.log(response)
      })
      .catch(function (error) {
        console.log(error)
      })
    

    但token也非绝对安全,因为就算token藏的再隐蔽,但是这一切都会暴露在前端,所以黑客可根据网站前端代码进行分析,然后写一套脚本自动化抓取token,然后对服务端接口实施攻击。

  4. 验证码

    验证码是一种非常强大的防范CSRF的方式,例如各种图形验证码(12306的高难度图形验证),可以达到有效的防御功能。

    但是不可能所有接口都是用验证码,这样用户体验会很差,所以 比较好的方式是尽量减少图形验证码的使用,对于一些操作不敏感的接口使用token+referrer来防御。

    但是图形也不是绝对安全,因为目前图像识别已经很先进,简单的还是可以破解的...但是为了安全,还是需要进行各种防御的设置。