关于使用内联 JavaScript 的思考
我们的开发团队正在开发一个新的网络相关项目。在这个项目中,安全具有非常高的优先级。然而,在浏览器支持方面实现内容安全策略 (CSP) 变得既痛苦又麻烦。
众所周知,引入 CSP 是为了通过阻止所有外部 CSS/JavaScript 文件并阻止执行内联脚本来减轻 XSS 注入(inject)。指令允许我们定义浏览器将执行的内容和来源的白名单(例如“自我”、域、协议(protocol)等)。
然而,执行内联 JavaScript 代码是一个完全不同的故事。 CSP 确实允许使用“unsafe-inline”指令执行内联代码。然而,添加这个指令违背了实现 CSP 的目的。
为了克服这一障碍,我们的团队花了一些时间讨论可能和可行的解决方案。
在下面的段落中,我们将讨论一些解决方案。欢迎对这些解决方案提出任何建议和反馈。
提案 #1:外部文件
在外部文件中提供特定于页面的代码。该文件将在每个请求中生成,因为它包含动态数据(例如 Google Maps key 、GA 跟踪片段、php-debugbar)。
以下习语说明了如何在 HTML 代码中实现此提议。
<html>
<head>
<script src="/generated/external-file.js"></script>
</head>
</html>
优点:客户端将保持透明,因为服务器必须生成文件。
缺点: HTTP 是一种无状态协议(protocol),随着每个请求而变化的动态数据不能包含在外部文件中(例如 php-debugbar)。
提案#2:Nonce 与 CSP 结合使用
CSP 的第 2 级通过在 CSP 响应 header 中提供随机数来支持内联样式和脚本。
以下习语说明了一个内联脚本,其 nonce 属性包含一个动态生成的值。
<script nonce="nm77q3oep8l0ybxmugzewkfyacyma3n3">console.log('Hello world');</script>
为了执行此脚本,CSP 响应 header 中需要存在随机数。
以下习语说明了如何将此随机数合并到响应 header 中。
Content-Security-Policy: script-src 'nonce-nm77q3oep8l0ybxmugzewkfyacyma3n3'
出于安全原因,这个 nonce 必须是随机的,并且必须在每次请求时重新生成。
优点:由 CSP 的 2 级支持。
缺点:浏览器支持是一个问题(75% 的浏览器实现了 CSP 级别 2,日期为 2017 年 4 月 10 日 (http://caniuse.com/#feat=contentsecuritypolicy2)。
提案 #3:与 CSP 一起进行哈希处理
CSP 的第 2 级通过散列这些元素的内容并在 CSP 响应 header 中提供此散列来支持内联样式和脚本。
以下习语说明了一个内联脚本,其内容将产生以下 SHA-256 哈希值:9e8a3b5e27971b7309ff6c00f5c80644ffe8e635ce797d7eed8ee23d485a19f2
<script>console.log('Hello world');</script>
为了执行此脚本,计算出的哈希值需要出现在 CSP 响应 header 中。
以下习语说明了如何将此散列合并到响应 header 中。
Content-Security-Policy: script-src 'sha256-9e8a3b5e27971b7309ff6c00f5c80644ffe8e635ce797d7eed8ee23d485a19f2;'
优点:受 CSP 级别 2 支持,并允许通过使用 HTTP 加速器缓存页面。
缺点:浏览器支持是一个问题(75% 的浏览器实现了 CSP 级别 2,日期为 2017 年 4 月 10 日 (http://caniuse.com/#feat=contentsecuritypolicy2)。
提案 #4:JSON
CSP 仅禁止包含可执行 JavaScript 代码的脚本元素,简而言之,这意味着允许脚本元素中的配置数据。
以下习语说明了包含 JSON 数据的脚本元素。
<script type="application/json">
{
"googleMapsKey": "v1zs9Bc10hMZ073S14gy",
"analyticsProperty": "UA-192348"
}
</script>
见 https://mathiasbynens.be/notes/json-dom-csp有关更多信息和示例。
优点:非常安全,因为它不允许脚本标签内的可执行代码。
缺点:此解决方案未解决为每个请求(例如 php-debugbar)动态生成的 JavaScript 代码。
提案 #5:自定义随机数实现
实现自定义 nonce 解决方案,在大多数浏览器支持 CSP 级别 2 之前可能会使用该解决方案。
以下习语说明了如何实现此解决方案。
见 JSFiddle code (适应工作)和或 Gist与原始代码。
为了执行此脚本,必须在响应 header 中提供“unsafe-eval”指令。尽管不推荐这样做,但此方法支持此方法。除了许多框架,如 Vue.js require工作指令。
以下习语说明了必须如何将指令合并到响应 header 中。
Content-Security-Policy: script-src 'unsafe-eval'
优点:支持 CSP 级别 1,当大多数浏览器支持 CSP 级别 2 时,可以轻松适应提案 #2。
缺点:自定义实现必须经过彻底测试,并且需要在 CSP header 中使用“unsafe-eval”指令。
请分享您的想法,我们会接受任何反馈!
最佳答案
可以逐页定义内容安全策略:您可以根据特定页面的特定需求微调特定页面的策略。
外部文件
我建议先将所有“非动态”内联样式和脚本移动到外部文件,而不是试图找到解决所有问题的解决方案。内容安全策略的存在是因为内联脚本并不总是可信的。除了与 CSP 配合使用外,使用外部文件还有许多优点:
沙盒
另一个值得研究的“解决方案”是沙箱。如果
sandbox
指令存在,页面被视为加载在 <iframe>
中与 sandbox
属性。有关沙盒的更多信息,请访问:HTML5 specification .
更多信息
Google 开发者指南:Content Security Policy .
Mozilla 开发者指南:Content Security Policy .
W3C 的 Web 应用程序安全工作组已经开始着手该规范的下一次迭代,Content Security Policy Level 3 .
关于javascript - 内联 JavaScript 与 CSP 级别 1 结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43326887/