实施内容安全策略

附加组件团队最近完成了在 addons.mozilla.org (AMO) 上启用 内容安全策略 (CSP) 的工作。本文旨在介绍实施 CSP 的基础知识,并重点介绍我们在 AMO 上实施 CSP 时遇到的某些问题。

什么是内容安全策略?

内容安全策略 (CSP) 是一种安全标准,旨在帮助防止 跨站点脚本 (XSS) 和其他内容注入攻击。它通过将用户代理加载的内容来源限制为仅限站点运营商允许的来源来实现这一点。

该策略是通过与服务器响应一起发送的标头来实现的。从那里开始,支持的用户代理需要接管该策略并积极阻止检测到的策略违规。

为什么需要它?

CSP 是另一层防御,有助于保护用户免受各种攻击载体的攻击,例如 XSS 和其他形式的内容注入攻击。虽然它不是灵丹妙药,但它确实有助于使攻击者更难注入内容和窃取数据。

安全地构建网站很困难。即使您知道一般 Web 安全最佳实践,仍然很容易忽略某些内容或无意中在原本安全的网站中引入安全漏洞。

CSP 通过限制可以从其加载活动内容和被动内容的来源来起作用。它还可以限制活动内容的某些方面,例如执行内联 JavaScript 和使用eval().

实施 CSP

要实施 CSP,您必须为网站使用的所有类型资源定义允许来源列表。例如,如果您有一个简单的网站,需要加载本地托管的脚本、样式表和图像,以及从 jQuery 库的 CDN 加载,您可以使用以下内容:

Content-Security-Policy:<br>    <b>default-src</b> 'self';<br>    <b>script-src</b> 'self' https://code.jqueryjs.cn;

在上面的示例中,Content-Security-Policy是 HTTP 标头。您也可以指定Content-Security-Policy-Report-Only,这意味着用户代理将报告错误,但不会积极阻止任何内容。在测试新策略时,这是一个有用的功能。

对于script-src,我们还必须明确列出'self',因为如果您定义了指令,那么它将不再从default-src.

继承。始终定义default-src非常重要。否则,指令将默认允许所有资源。因为我们有default-src 'self',这意味着从网站域提供的图像也将被允许。

default-src是源指令在未配置时将回退到的特殊指令。但是,以下指令不会从default-src继承,因此请注意这一点,并记住不将它们设置为任何内容意味着它们将被取消设置或使用浏览器的默认设置

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

'self'设置为default-src通常是安全的,因为您控制自己的域。但是,如果您确实想默认更严格地锁定内容,您可以使用default-src 'none'并明确列出所有已知的资源类型。鉴于上面的示例,这将导致如下所示的策略:

Content-Security-Policy:<br>    <b>default-src</b> 'none';<br>    <b>img-src</b> 'self';<br>    <b>script-src</b> 'self' https://code.jqueryjs.cn;<br>    <b>style-src</b> 'self';
如果您依赖于预取,则可能会遇到default-src 'none'问题。在 AMO 上,我们发现 Firefox 中的浏览器预取不会被识别为特定内容类型,因此会回退到default-src。如果default-src不包含所涉及的来源,则预取的资源将被阻止。有一个 有关此问题的更多信息的开放错误

处理内联脚本

CSP 默认情况下不允许内联 JavaScript,除非您明确允许它。这意味着您需要删除以下内容

  • <script>页面中的块
  • HTML 中的 DOM 事件处理程序,例如onclick
  • javascript伪协议。

如果您确实需要允许它,则 CSP 提供了一种通过使用nonce-sourcehash-source指令,允许执行特定内容块。您可以通过使用‘unsafe-inline’script-src指令中选择退出此保护,但强烈建议不要这样做,因为它会使您的网站容易受到 XSS 攻击。

有关nonce-sourcehash-source的更多信息,请参阅 我们拥有的 Web CSP

处理 eval()

CSP 还阻止动态脚本执行,例如

  • eval()
  • 用作setTimeout / setInterval
  • new Function()构造函数

的第一个参数的字符串如果您需要启用此功能,可以使用'unsafe-eval',但同样不建议这样做,因为不受信任的代码很容易潜入块中。

在 AMO 上,我们发现许多库代码都使用 eval 和 new Function,而这部分 CSP 实现是最耗时修复的部分。例如,我们有使用new Function的下划线模板。修复这些问题需要我们迁移到预编译的模板。

处理层叠样式表 (CSS)

CSP 默认情况下不允许

  • <style>
  • styleHTML 中的属性

这对我们来说是一个更大的问题。许多库在使用 JavaScript 添加到页面的 HTML 代码段中使用 style 属性,我们在 HTML 模板中直接添加了少量 style 属性。

值得一提的是,如果样式属性是通过 JavaScript 直接更新的,那么您就不会遇到问题。例如,jQuery 的css()方法就可以了,因为它在幕后直接更新了样式属性。但是,您不能在 JS 添加的 HTML 代码块中使用style="background: red"

这可能有点令人困惑,因为在 Firefox 检查器中,通过 JavaScript 添加的样式属性看起来与 HTML 中的 style 属性相同。

与之前一样,如果您需要一种受控的方法来允许选择性地使用内联 CSS,则可以使用nonce-sourcehash-source指令。

您可能正在想:“这是 CSS,有什么风险?” 有各种巧妙的方法可以使用 CSS 从网站中窃取数据。例如,使用属性选择器和背景图像,您可以暴力破解和窃取属性敏感数据,例如 CSRF 令牌。有关此问题以及通过 CSS 进行的其他更高级攻击载体的更多信息,请参阅 XSS (No, the _other ‘S’)

对于'unsafe-inline',不建议用于style-src,但这是权衡风险和消除内联样式所需的更改次数的问题。

报告

最好设置report-uri指令并将其指向某个地方以收集 CSP 违规的 JSON 报告。由于 CSP 目前不会合并错误报告,因此具有多个错误的单个页面会导致向您的报告端点发送多个报告。如果您运营的是拥有大量受众的网站,那么该端点可能会收到大量流量。

除了由实际违规触发的报告之外,您还会发现许多附加组件和浏览器扩展程序会导致 CSP 违规。最终结果是大量噪音:拥有允许对传入数据进行过滤的功能非常推荐。

测试

创建初始策略后,下一步是测试它并修复任何缺失的来源。如果您运营的是大型网站,您可能会对从其提取资源的来源数量感到惊讶。在报告模式下运行网站允许您在它们积极阻止内容之前通过控制台和 CSP 报告来捕获问题。

一旦每个人都确认没有被错误地阻止任何内容,就该强制执行策略了。从那时起,这仅仅是注意任何遗漏的内容并将策略与浏览器对 CSP 中某些新功能的支持保持同步。

实施

在您确定了正常工作的策略后,下一步是配置您的系统以提供 CSP 指令。实施方式因您选择的 Web 服务器软件而异,但它通常看起来像这样

# Enable CSP in Apache
Header set Content-Security-Policy "default-src 'none'; img-src 'self';
    script-src 'self' https://code.jqueryjs.cn; style-src 'self'"
# Enable CSP in nginx
add_header Content-Security-Policy "default-src 'none'; img-src 'self';
    script-src 'self' https://code.jqueryjs.cn; style-src 'self'";

如果您的服务提供商不提供对您的 Web 服务器配置的控制,请不要惊慌!您仍然可以通过使用meta标签启用 CSP。只需让您的meta标签成为<head>:

<!-- Enable CSP inside the page's HTML -->
<head>
    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self';
          script-src 'self' https://code.jqueryjs.cn; style-src 'self'">
</head>

中的第一个标签

我们的最终实现

Content-Security-Policy:<br>    <b>default-src</b> 'self';<br>    <b>connect-src</b> 'self' https://sentry.prod.mozaws.net;<br>    <b>font-src</b> 'self' https://addons.cdn.mozilla.net;<br>    <b>frame-src</b> 'self' https://ic.paypal.com https://paypal.com<br>        https://www.google.com/recaptcha/ https://www.paypal.com;<br>    <b>img-src</b> 'self' data: blob: https://www.paypal.com https://#<br>        https://addons.cdn.mozilla.net https://static.addons.mozilla.net<br>        https://ssl.gstatic.com/ https://sentry.prod.mozaws.net;<br>    <b>media-src</b> https://videos.cdn.mozilla.net;<br>    <b>object-src</b> 'none';<br>    <b>script-src</b> 'self' https://addons.mozilla.org<br>        https://www.paypalobjects.com https://apis.google.com<br>        https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/<br>        https://# https://addons.cdn.mozilla.net;<br>    <b>style-src</b> 'self' 'unsafe-inline' https://addons.cdn.mozilla.net;<br>    <b>report-uri</b> /__cspreport__

鉴于 AMO 是一个较旧且非常复杂的网站,您可能想知道我们的最终策略是什么样子

哇!正如您想象的那样,进行了大量的测试才能发现 AMO 使用的无数资源。

总结

您的网站越旧,设置和遵守合理的 Content Security Policy 所需的工作就越多。但是,值得花时间,因为它是在深度防御理念的支持下提供的额外安全层。

MDN 关于 CSP 的文档

IRC:四月

更多由 April King 撰写的文章…

关于 Stuart Colville

Stuart 是 Firefox 扩展的工程经理。

更多由 Stuart Colville 撰写的文章…


11 条评论

  1. Scott Helme

    很棒的文章!很高兴看到组织带头使用 CSP 并展示它带来的益处。我认为 CSP 报告也非常重要,但正如您提到的,它可能非常嘈杂,生成大量流量,并且能够过滤传入报告很重要。我设置了一项免费服务,允许网站收集 CSP 报告并对其进行过滤,同时以易于使用的格式呈现数据。您可以在 https://report-uri.io 找到它。

    希望这将通过为网站所有者提供收集可用宝贵数据的手段来提高 CSP 部署的价值。

    2016 年 2 月 16 日 下午 12:17

  2. barretlee

    不错!

    2016 年 2 月 16 日 下午 4:32

  3. Lucas Rinaldi

    感谢您的文章。CSP 对我们开发者非常有益。不错的入门方式。

    2016 年 2 月 18 日 上午 5:16

  4. Earl

    恭喜… 搞坏了我的书签,它们只是将页面滚动到顶部和底部。点赞!

    2016 年 2 月 18 日 上午 7:56

  5. April King

    @Earl: 存在一个关于此问题的 Bugzilla 错误,如果您愿意,可以评论或通过邮件获取更新

    https://bugzilla.mozilla.org/show_bug.cgi?id=866522

    2016 年 2 月 18 日 上午 8:32

  6. Koemsie Ly

    感谢您精彩的文章。CSP 对开发者非常有用。不错的入门方式。

    2016 年 2 月 23 日 下午 8:57

  7. Seth Berger

    感谢 April 和 Stuart 的精彩文章,也感谢 Scott 的文章以及您很棒的网站测试服务!我已着手为自己的服务 OurEvents 实施 CSP。

    你们知道如何在明确由网站 CSS 文件(如 style-src 允许的那样)指定的情况下允许 img-src data:,但阻止其他情况下使用 data: 图像吗?

    如果您查看 jQuery Mobile CSS 文件,您会看到很多以 data:image/svg+xml 格式表示的图标。当然,我可以阻止 inlineSVG 并使用它们的 PNG 图标,但这样加载每个图标都需要向 Web 服务器发出另一个请求。

    理想情况下,应该有一个 img-data-src:’sitecss’ 指令,允许浏览器使用在明确指定的 CSS 文件中找到的任何 data: 图像。如果没有这个,我不知道如何允许某些 data: 图像,但阻止其他图像。

    2016 年 2 月 26 日 上午 8:19

  8. Dan Veditz

    Seth: CSP 中没有指定的方法来区分 HTML 图像标签、DOM 注入的图像和 CSS 指定的图像。您可以尝试在 public-webappsec 邮件列表 (w3.org) 上引发兴趣,但我怀疑大多数人会发现用例太窄,无法证明额外的复杂性。允许 data: 用于图像并不危险:它们充其量只能毁掉您的网站,如果您有允许数千字节注入的漏洞。不会运行任何脚本,如果它不是有效的图像数据,则不会显示。

    2016 年 2 月 26 日 上午 9:37

  9. Seth Berger

    Dan: 不幸的是,在过去几年中,许多流行的浏览器都存在图像渲染漏洞。我认为添加字符串 img-data-src:’sitecss’ 不会增加太多复杂性。

    另一个问题是:像图像一样,浏览器在字体方面也存在漏洞,因此为了安全起见,我将 font-awesome 托管在本地,以防止网站 UI 发生意外更改。但是,如果我可以指定具有字体文件 URL、文件大小和文件哈希值的 CSP font-src 规则,以及指定我本地副本的字体作为备份,我会考虑使用 BootstrapCDN。这可能吗?

    当然,除了 CDN 的 200 OK 响应之外,任何其他响应都应触发使用本地副本。例如,如果 CDN 通常通过 https 提供服务,但后来决定重定向到 http,则应触发使用通过 https 提供服务的本地副本。

    2016 年 2 月 26 日 上午 10:52

  10. Thor Brendan Bunnyson

    各位,你们有没有什么解决方案可以解决通过右键单击“将链接另存为...”下载插件的问题,这会完全破坏它,谢谢。

    2016 年 3 月 14 日 下午 10:14

  11. April King

    Thor - 我根本没有遇到这个问题。我可以将链接另存为... 并下载 XPI,没有任何问题。也许您安装了与之冲突的插件?

    我建议您尝试使用干净的配置文件,看看是否仍然存在此问题。

    2016 年 3 月 15 日 上午 7:00

本文的评论已关闭。