javascript - 内联 JavaScript 与 CSP 级别 1 结合使用

标签 javascript json http inline content-security-policy

关于使用内联 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/

    相关文章:

    javascript - 在javascript中单击按钮更改文本并替换按钮/文本?

    mysql - 如何在没有 URL 的情况下使用 JSON 编码数组?

    java - 使用 Picasso 调整图像大小并添加到 HttpPost

    java - 通过代理java创建套接字

    javascript - JQuery CSS 两个值

    javascript - 与 hell 相比,Svelte 'evaluate script' 时间似乎更长,preact

    javascript - Cordova、Android 和无缝/无缝的声音循环

    c# - 使用 propertyname.subname 创建 JObject

    json - 如何使用 serde_json 将 "NaN"反序列化为 `nan`?

    Jquery ajax从https到http的调用