FreeBuf 文章:如何防止XSS攻击?

Author Avatar
Tr0y 11月 03, 2018 19:21:49 本文共 3.2k 字
  • 文为知己者书
  • 在其它设备中阅读本文章

前端安全系列(一):如何防止XSS攻击?[美团技术团队]

原文

笔记

前端安全

  • CSP:内容安全策略
  • Same-Site Cookies
  • XSS 攻击的介绍

    通常页面中包含的用户输入内容都在固定的容器或者属性内,以文本的形式展示。

    攻击者利用这些页面的用户输入片段,拼接特殊格式的字符串,突破原有位置的限制,形成了代码片段。

    攻击者通过在目标网站上注入脚本,使之在用户的浏览器上运行,从而引发潜在风险。

    通过 HTML 转义,可以防止 XSS 攻击(注意特殊的 HTML 属性、JavaScript API)

    做了 HTML 转义,并不等于高枕无忧。
    对于链接跳转,如 <a href=”xxx” 或 location.href=”xxx”,要检验其内容,禁止以 javascript: 开头的链接,和其他非法的 scheme。

    使用白名单。

    HTML 转义是非常复杂的,在不同的情况下要采用不同的转义规则。如果采用了错误的转义规则,很有可能会埋下 XSS 隐患。

    应当尽量避免自己写转义库,而应当采用成熟的、业界通用的转义库。

  • XSS 有哪些注入的方法:

    在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。

    在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。

    在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。

    在标签的 href、src 等属性中,包含 javascript: 等可执行代码。

    在 onload、onerror、onclick 等事件中,注入不受控制代码。

    在 style 属性和标签中,包含类似 background-image:url(“javascript:…”); 的代码(新版本浏览器已经可以防范)。

    在 style 属性和标签中,包含类似 expression(…) 的 CSS 表达式代码(新版本浏览器已经可以防范)。

  • 在处理输入时,以下内容都不可信

    来自用户的 UGC 信息
    来自第三方的链接
    URL 参数
    POST 参数
    Referer (可能来自不可信的来源)
    Cookie (可能来自其他子域注入)

XSS 的分类

根据攻击的来源,XSS 攻击可分为存储型、反射型和 DOM 型三种。

  • 存储型 XSS:

    • 攻击者将恶意代码提交到目标网站的数据库中。
    • 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
    • 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
    • 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

    这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。

  • 反射型 XSS

    • 攻击者构造出特殊的 URL,其中包含恶意代码。
    • 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
    • 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
    • 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

    反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。

    反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

    POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。

  • DOM 型 XSS

    • 攻击者构造出特殊的 URL,其中包含恶意代码。
    • 用户打开带有恶意代码的 URL。
    • 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
    • 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

    DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。

XSS 攻击的预防

XSS 攻击有两大要素:

  • 攻击者提交恶意代码
  • 浏览器执行恶意代码

防止 攻击者提交恶意代码

在用户提交时,由前端过滤输入,然后提交到后端。这样做是否可行呢?

答案是不可行。一旦攻击者绕过前端过滤,直接构造请求,就可以提交恶意代码了。

后端在写入数据库前,对输入进行过滤,然后把“安全的”内容,返回给前端。这样是否可行呢?比如用户输入了 5 < 7 这个内容,在写入数据库前,被转义,变成了 5 < 7。

在提交阶段,我们并不确定内容要输出到哪里。这里的 并不确定内容要输出到哪里 有两层含义:

  1. 用户的输入内容可能同时提供给前端和客户端,而一旦经过了 escapeHTML(),客户端显示的内容就变成了乱码(5 &lt; 7)。
  2. 在前端中,不同的位置所需的编码也不同。当 5 &lt; 7 作为 HTML 拼接页面时,可以正常显示:<div title = "comment" > 5 &lt; 7 </div>;当 5 &lt; 7 通过 Ajax 返回,然后赋值给 JavaScript 的变量时,前端得到的字符串就是转义后的字符。这个内容不能直接用于 Vue 等模板的展示,也不能直接用于内容长度计算。不能用于标题、alert 等。
    输入侧过滤能够在某些情况下解决特定的 XSS 问题,但会引入很大的不确定性和乱码问题。在防范 XSS 攻击时应避免此类方法。
    但是,对于明确的输入类型,例如数字、URL、电话号码、邮件地址等等内容,进行输入过滤还是必要的。

防止 浏览器执行恶意代码

  1. 防止 HTML 中出现注入
  2. 防止 JavaScript 执行时,执行恶意代码

预防 存储型反射型 XSS 攻击

存储型和反射型 XSS 都是在服务端取出恶意代码后,插入到响应 HTML 里的,攻击者刻意编写的“数据”被内嵌到“代码”中,被浏览器所执行。

预防这两种漏洞,有两种常见做法:

  1. 改成纯前端渲染,把代码和数据分隔开。
    纯前端渲染的过程:

     1. 浏览器先加载一个静态 HTML,此 HTML 中不包含任何跟业务相关的数据。
     2. 然后浏览器执行 HTML 中的 JavaScript。
     3. JavaScript 通过 Ajax 加载业务数据,调用 DOM API 更新到页面上。
    

    在纯前端渲染中,我们会明确的告诉浏览器:下面要设置的内容是文本(.innerText),还是属性(.setAttribute),还是样式(.style)等等。浏览器不会被轻易的被欺骗,执行预期外的代码了。

    但纯前端渲染还需注意避免 DOM 型 XSS 漏洞(例如 onload 事件和 href 中的 javascript:xxx 等,请参考下文”预防 DOM 型 XSS 攻击“部分)。

    在很多内部、管理系统中,采用纯前端渲染是非常合适的。但对于性能要求高,或有 SEO 需求的页面,我们仍然要面对拼接 HTML 的问题。

  2. 对 HTML 做充分转义。
    如果拼接 HTML 是必要的,就需要采用合适的转义库,对 HTML 模板各处插入点进行充分的转义。


预防 DOM 型 XSS 攻击:

DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。

在使用 .innerHTML.outerHTMLdocument.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent.setAttribute() 等。

DOM 中的内联事件监听器,如 locationonclickonerroronloadonmouseover 等,<a> 标签的 href 属性,JavaScript 的 eval()setTimeout()setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。

<!-- 内联事件监听器中包含恶意代码 --> 
< img   onclick = "UNTRUSTED"   onerror = "UNTRUSTED"   src = "data:image/png," >

<!-- 链接内包含恶意代码 --> 
< a   href = "UNTRUSTED" > 1 </ a > 

< script >  
// setTimeout()/setInterval() 中调用恶意代码 
setTimeout( "UNTRUSTED" )
setInterval( "UNTRUSTED" )

// location 调用恶意代码 
location.href =  'UNTRUSTED' 

// eval() 中调用恶意代码 
eval ( "UNTRUSTED" )
</ script > 

其他 XSS 防范措施

虽然在渲染页面和执行 JavaScript 时,通过谨慎的转义可以防止 XSS 的发生,但完全依靠开发的谨慎仍然是不够的。以下介绍一些通用的方案,可以降低 XSS 带来的风险和后果。

Content Security Policy

严格的 CSP 在 XSS 的防范中可以起到以下的作用:

禁止加载外域代码,防止复杂的攻击逻辑。
禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
禁止未授权的脚本执行(新特性,Google Map 移动版在使用)。
合理使用上报可以及时发现 XSS,利于尽快修复问题。

输入内容长度控制

对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度。

其他安全措施

  • HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
  • 验证码:防止脚本冒充用户提交危险操作。

总结

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

补缺补漏

CSP

  • CSP 是什么:

    CSP:内容安全策略(区别于 同源策略)。简单来说,就是我们能够规定,我们的网站只接受我们指定的请求资源。CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。

  • CSP 的分类

    • Content-Security-Policy:配置好并启用后,不符合 CSP 的外部资源就会被阻止加载。
    • Content-Security-Policy-Report-Only:不执行限制选项,只是记录违反限制的行为。它必须与 report-uri 选项配合使用。
  • CSP 的使用
    • 在 HTTP Header 上使用(首选):
      Content-Security-Policy: 策略
      Content-Security-Policy-Report-Only: 策略
      
    • 在 HTML 上使用:
      <meta http-equiv="content-security-policy" content="策略">
      <meta http-equiv="content-security-policy-report-only" content="策略">
      
      Meta 标签与 HTTP 头只是行式不同而作用是一致的,如果 HTTP 头与 Meta 定义同时存在,则优先采用 HTTP 中的定义。
      如果用户浏览器已经为当前文档执行了一个 CSP 的策略,则会跳过 Meta 的定义。如果 META 标签缺少 content 属性也同样会跳过。

SameSite-cookies

SameSite-cookies 是一种机制,用于定义 cookie 如何跨域发送。这是谷歌开发的一种安全机制,并且现在在最新版本(Chrome Dev 51.0.2704.4)中已经开始实行了。SameSite-cookies 的目的是尝试阻止 CSRF(Cross-site request forgery 跨站请求伪造)以及 XSSI(Cross Site Script Inclusion (XSSI) 跨站脚本包含)攻击。

End

What do you think?

本文标题: FreeBuf 文章:如何防止XSS攻击?
原始链接: http://www.tr0y.wang/2018/11/03/XSSMeiTuan/
发布时间: 2018.11.03-19:21
最后更新: 2018.11.04-16:27
版权声明: 本站文章均采用CC BY-NC-SA 4.0协议进行许可。转载请注明出处!