GMTC北京站两周后开幕,58个议题全部上线,点击查看 了解详情
写点什么

Dropbox 的 Web 安全防护策略之二:unsafe-inline 指令和随机数配置

2015 年 12 月 01 日

【编者的话】Dropbox 的 Web 安全防护措施之一是使用基于内容的安全策略(CSP)。Dropbox 的安全工程师 Devdatta Akhawe 通过四篇文章,介绍了 CSP 在 Dropbox 中推广的细节和经验。Dropbox 的 CSP 原则大大减少了 XSS 和内容注入攻击。不过,大规模使用比较严苛的 CSP 规则将面临诸多挑战。我们希望通过这四篇 CSP 系列文章,将 Dropbox 在实践 CSP 过程中的收获分享给广大开发社区朋友。第一篇文章主要介绍如何在规则中设置报表筛选管线来标记错误;第二篇介绍 Dropbox 如何在上述规则中配置随机数及缓解unsafe-inline带来的安全风险;第三篇介绍如何降低unsafe-eval造成的风险,以及介绍 Dropbox 所开发的开源补丁;最后一篇介绍在权限分离机制下,如何减小第三方软件整合时的风险。本篇是该系列文章的第二篇,主要介绍 Dropbox 的随机数配置,还有 unsafe-inline 指令所带来的问题及解决方法。

第一篇文章中,我们讨论了如何筛选错误报告及如何利用CSP 为网站配置内容源白名单。通常而言,白名单中最重要的内容源是代码源,代码源由 script-srcobject-src指令确定。标准的 CSP 配置一般含有一个受信任域名的列表,其中包括主网站名和可靠 CDN 等,它们由script-src及其他诸如unsafe-inlineunsafe-eval的指令给出。

这种规则阻止了随意注入的第三方脚本,但又由于有unsafe-inline这项指令存在,它并不能有效地防止 XSS 攻击。如果读者对unsafe-inlinenonce-src尚不熟悉,建议参阅这篇介绍文章。简而言之,CSP 默认阻止所有内联脚本区块和内联事件( < div onclick="somescript" >),加强了代码和数据的分离。所有运行在网页中的代码必须来自白名单中的源所提供的脚本文件。这样便大大降低了 XSS 攻击的风险,但同时也需要巨大的移植工作量。

为了使移植变得简便,规则中允许一类特殊的源指令句法,它适用于script-srcstyle-src指令,如果含有匹配的随机数属性,那么则接受内联内容。这样, script-src源列表中含有nonce-randomnumber的规则就可以允许执行将“randomnumber”作为随机数属性值的脚本标记。随机数句法是 CSP2 的一部分,现在仅仅在 Chrome 和 Firefox 中支持。而在其他浏览器中,使用内联脚本标记需要用unsafe-inline使能所有内联脚本。

本篇文章将讨论我们在 Dropbox 网站上配置随机数的经验。在进入细节讨论之前,必须强调 CSP 只是一项缓解措施,并不能取代严格的验证及清理净化工作。Dropbox 服务器和客户端分别使用的库是 Pyxl React ,客户端的 HTML 清理使用的库则是 DOMPurify

配置脚本随机数

配置脚本随机数包含两个步骤:一、给所有内联脚本标记加上对应的随机数;二、移除所有内联事件处理器。Dropbox 使用 Pyxl 生成服务器端的 HTML。Pyxl 将 HTML 标签转换为 Python 对象,在清理过程中自动去除非可信数据。我们调整了一些 Pyxl 清理代码,以便在脚本标记中插入正确的脚本随机数。

第二步移除内联事件处理器比较困难。有较长的一段时间我们极力反对使用内联事件处理器,Dropbox 上新写的代码也没有它们,但这仍然造成了大量旧代码没有办法移植。为了减少过渡阶段的工作量,我们决定自动重写内联事件处理器来使用内联脚本标记。最基本的例子如下,原来的代码<div id="foo” onload="somescript">变为:

复制代码
<div id="foo"><!-- if id is absent, we create a unique id -->
<script>
// The nonce in the script tag above will be
// inserted during Pyxl serialization
function(){
var e = document.getElementById(foo);
e.addEventListener("load", function(ev){
// ...somescript..
});
}();
</script>

上述例子有几个值得关注的点。首先,我们使用了即时调用函数表达式,这样不会造成全局命名空间的混乱。第二,我们插入了一个脚本标记,它在原本的标记打开后加入了onload事件处理器。通常的加入操作是在原来的 div 元素结束或DOMContentLoaded事件之后,虽然它们也可能有很好的特性,但我们修改后的代码已经很接近浏览器原来的性能,因此这里我们使用的方法适用于自动转换。

读者们可能仍然会有疑问:以上转换没有在onload代码中修复已有的 XSS 攻击,所以并不是完全安全的转换。然而,我们认为这种危险是在可接受范围内,因为像 Pyxl 之类的系统,已经可以很好地识别并阻止在服务器的代码中的 XSS。另外,随着时间的推移,我们仍将舍弃内联事件处理器,从而彻底规避这类风险。

通过上述的代码转换,只有在我们掌控中的内联脚本和事件处理器会执行。如果一次攻击尝试插入事件处理器(比如使用了 DOMXSS 等),支持随机数属性的浏览器将会阻止这类攻击。

内联脚本错误报告

和所有的 CSP 配置一致,推出一个新的随机数属性通常的做法是先在 report-only 模式下运行。Dropbox 使用 report-only 模式将近一个月。同样也和内容源错误报告一样,内联脚本错误中的噪声滤除也十分重要。

除了上篇文章中提到的技巧,Chrome 通过发送另外两项内容域(fields)来辅助识别内联脚本错误:脚本样本和源文件。脚本样本对于代码库中的快速查找十分关键,因为在本地重现问题较为困难。源文件则指的是通过 DOM API 插入内联脚本的 JavaScript 源文件。在筛选内联脚本错误阶段,我们过滤掉所有源文件域为 URI 的报告,这类错误报告不属于当前讨论的应用范围(广告插入和扩展工具是一类普遍的错误源)。类似地,参照 Neil 的建议,有些报告的脚本样本中包含的明显不是自己应用的代码(比如,脚本中包含字符串“lastPass”),我们也过滤掉了这一类错误报告。

Firefox 有一个程序缺陷:即便 nonce src允许执行内联脚本,Firefox 仍会上报错误,同时也在执行代码。因此我们建议,为了减少噪声,在配置随机数时删除 Firefox 的report-uri

更新:这个程序缺陷已经修复了!不过修复的版本是 2015 年十月发布的 Firefox 43,我们建议在 2016 年 1 月之前,不要为 Firefox 发送report-uri

利用合适的过滤器,我们给我们的规则配置了随机数源,而且开始查找错误并修复。对于像 Dropbox 这样巨大的规模和所拥有的代码库来说,这个过程十分枯燥。但是这项任务的回报值得我们做出努力,而且工作量也并非无法计算。经过几周的努力,我们成功地在执行模式下配置了nonce-src

无随机数支持下 DOMXSS 攻击的缓解

比较可惜的是,随机数源是 CSP2 中的功能。虽然 Chrome 和 Firefox 已经支持随机数源一段时间了,但 Safari 及 Edge 仍未支持。Safari 和 Edge 都没有随机数源句法,却强化了unsafe-inline源表达式。为了使我们的网页应用性能更好,我们使用了另一个技巧:

复制代码
document.addEventListener('DOMContentLoaded', function () {
var metaTag = document.createElement('meta');
metaTag.setAttribute('http-equiv', 'Content-Security-Policy');
metaTag.setAttribute('content', "script-src https: 'unsafe-eval';");
document.head.appendChild(metaTag);
});

简单地说,在浏览器执行所有 HTML 和同步脚本(包括内联脚本)后,激发 DOMContentLoaded 事件。在此之后,其他的 JavaScript 任务和事件进行排队,或者激发其他的 onload 处理器。随后的操作性能得到最大化,我们不再同时执行远程脚本,所以大部分 JavaScript 代码都在DOMContentLoaded之后执行。

上面展示的代码在DOMContentLoaded激发之后,插入了第二代 CSP 规则。需要特别说明的是,第二代 CSP 规则没有“unsafe-inline”。浏览器处理多个 CSP 规则时会强制遵循所有规则,只有符合全部规则的代码才能够得以执行。因此,在DOMContentLoaded事件激发之前,Safari 和 Edge 仅在初始 HTML 中解析并支持内联脚本。DOMContentLoaded激发之后,这两个浏览器便停止支持内联事件处理器。

设想一下,在某次攻击插入了含有恶意有效载荷(<div onclick=alert(1)>)。Chrome 和 Firefox 中的规则包含标头传递的随机数,可以阻止内联 onclick。而在 Safari 和 Edge 中,第一个规则允许onclick处理器,DOMContentLoaded事件后插入的第二个规则阻止onclick。尽管这种保护相对于支持随机数源的浏览器中的保护措施较为薄弱,但它仍然阻止了大量的 DOMXSS 攻击。

最后的思考

上面提到的方法有效地缓解了 Dropbox 网页应用受到的 XSS 攻击。在 Chrome、Firefox、Safari 还有 Edge 等浏览器中,我们的大量用户受到的网页攻击也得到了适当地减轻。我们修复了所有注入漏洞,也为我们大多数用户修筑了二次防线以抵御更强的注入攻击。

配置像 CSP 这样重要的工程,尤其还要舍弃“unsafe-inline”,需要全公司的鼎力支持。感谢参与该项目的所有 Dropbox 成员,特别是安全工程团队的小伙伴们。这篇文章是实践 CSP 系列文章中的第二篇。下一篇,我们将介绍在规则中加入unsafe-eval的影响,以及如何它所带来的降低风险。

查看英文原文: [CSP] Unsafe-inline and nonce deployment

编后语

《他山之石》是 InfoQ 中文站新推出的一个专栏,精选来自国内外技术社区和个人博客上的技术文章,让更多的读者朋友受益,本栏目转载的内容都经过原作者授权。文章推荐可以发送邮件到 editors@cn.infoq.com。


感谢魏星对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群(已满),InfoQ 读者交流群(#2))。

2015 年 12 月 01 日 16:391821
用户头像

发布了 268 篇内容, 共 102.5 次阅读, 收获喜欢 18 次。

关注

评论

发布
暂无评论
发现更多内容

深入剖析go中字符串的编码问题——特殊字符的string怎么转byte?

Gopher指北

go golang 后端 string utf-8

架构师训练营第三小结(9.28-10.4)

zjzj2017

LeetCode题解:226. 翻转二叉树,递归,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

发几张国庆的照片

亨利笔记

容器 k8s Harbor 镜像

爆赞!这份《Java核心宝典》绝对是面试复习的最佳选择

Java架构之路

Java 程序员 面试 编程语言

缓存服务-技术专题-解决方案

李浩宇/Alex

架构师训练营第三周作业(9.28-10.4)

zjzj2017

架构师训练营第三周课后作业

Gosling

极客大学架构师训练营

Python 为什么不支持 switch 语句?

Python猫

Python 编程

架构师1期-代码重构作业

ltl3884

极客大学架构师训练营

Redis-技术专题- 热点Key如何解决

李浩宇/Alex

入行架构师之前,这7项技能你要先了解一下

Java架构师迁哥

极客时间架构 1 期:第 3 周代码重构 - 学习总结

Null

Malagu 框架介绍

木香丘

云计算 开源 Serverless 架构 框架

架构师训练营第四周学习总结

尹斌

有这些要素,架构才完整

北风

架构 架构师之道 架构方法

四面阿里成功定级P6,月薪36K,分享面经(含面试题答案)

Java成神之路

Java 阿里巴巴 程序员 算法 编程语言

基于 Spring Boot 的企业级快速开发框架 BDF3

木香丘

架构 Spring Boot 可视化 后台管理系统

Serverless 多云解决方案 Malagu

木香丘

云计算 Serverless 架构 云原生 Malagu

Hazelcast IMDG 带你瞬间进入内存计算的时代

张磊

分布式计算 内存管理 分布式缓存 分布式内存网格

实用威胁建模指南(一)

亚伦碎语

敏捷 安全设计 系统安全 #威胁建模

使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源(二)

mzlogin

jsDelivr CDN Jekyll GitHub Pages 个人博客

如何高质量学习与正确运用设计模式

木香丘

学习 设计模式 实战

架构师训练营第四周作业

尹斌

spring-boot-route(九)整合JPA操作数据库

Java旅途

Java Spring Boot jpa

月薪60k的Java开发在阿里是什么级别?对技术能力有哪些要求?

Java成神之路

Java 阿里巴巴 程序员 面试 编程语言

java安全编码指南之:方法编写指南

程序那些事

java安全编码 java安全 java安全编码指南

第四周作业

极客大学架构师训练营

Redis-技术专题-基础介绍

李浩宇/Alex

单例模式

魏小龙

架构师训练营第 1 期第 4 周学习总结

好吃不贵

Service Mesh的演化与未来

Service Mesh的演化与未来

Dropbox的Web安全防护策略之二:unsafe-inline指令和随机数配置-InfoQ