https://www.html5rocks.com/en/tutorials/security/content-security-policy/
Content Security Policy (CSP)
一般来说,CSP作为黑客/白名单机制,用于扩展程序加载或执行的资源。
Web的安全模式植根于同一起源策略。代码https://mybank.com只能访问https://mybank.com数据,并且https://evil.example.com绝对不允许访问。每个起源与网络的其余部分保持隔离,为开发人员提供了一个安全的沙箱,用于构建和播放。在理论上,这是非常辉煌的。实际上,攻击者发现了颠覆系统的聪明方法。
例如,跨站脚本(XSS)攻击绕过同一起源策略,通过欺骗网站将恶意代码与预期内容一起发送。这是一个巨大的问题,因为浏览器将页面上显示的所有代码信任为该页面安全来源的合法部分。该XSS小抄是一个攻击者可能利用通过注入恶意代码来破坏这种信任的方法的旧有代表性的横截面。如果一个攻击者成功地注入了任何代码,那么它几乎是游戏过程:用户会话数据被泄露,应该保密的信息被过滤到坏人。我们显然希望如果可能的话。
本教程强调了一个有希望的新防御,可以显着降低XSS攻击在现代浏览器中的风险和影响:内容安全策略(CSP)。
来源白名单
XSS攻击利用的核心问题是浏览器无法区分旨在成为应用程序一部分的脚本以及第三方恶意注入的脚本。例如,本文顶部的Google +1按钮从https://apis.google.com/js/plusone.js本页面的上下文加载并执行代码。我们相信该代码,但我们不能指望浏览器找出自己的代码apis.google.com是真棒,而代码apis.evil.example.com可能不是。浏览器愉快地下载并执行页面请求的任何代码,无论来源如何。
CSP定义了HTTP头,而不是盲目信任服务器提供的所有信息,Content-Security-Policy可以允许您创建信任内容源的白名单,并指示浏览器仅从这些来源执行或呈现资源。即使攻击者可以找到一个注入脚本的孔,脚本也不会与白名单匹配,因此不会被执行。
由于我们信任apis.google.com提供有效的代码,我们相信自己也做同样的事情,我们定义一个策略,只允许脚本在来自这两个来源之一时执行:
Content-Security-Policy: script-src 'self' https://apis.google.com
简单吧?您可能猜到,script-src是一个指令,它控制特定页面的一组与脚本相关的权限。我们已经指定'self'了一个有效的脚本来源,https://apis.google.com另一个脚本。浏览器将尽可能地apis.google.com通过HTTPS从当前页面的起源下载并执行JavaScript 。
通过定义此策略,浏览器将简单地抛出错误,而不是从任何其他源加载脚本。当一个聪明的攻击者确实设法将代码注入您的网站时,她会长期运行错误信息,而不是她期望的成功:
政策适用于各种资源
虽然脚本资源是最明显的安全风险,但CSP提供了一组丰富的策略指令,可以对页面允许加载的资源进行相当精细的控制。你已经看到了script-src,所以这个概念应该是清楚的。我们快速浏览其余的资源指令:
- base-uri限制页面
元素中可能出现的网址。 - child-src列出了工作人员和嵌入式框架内容的URL。例如:child-src https://youtube.com可以嵌入来自YouTube的视频,但不能从其他来源嵌入视频。使用此代替不推荐使用的frame-src指令。
- connect-src 限制您可以连接的来源(通过XHR,WebSockets和EventSource)。
- font-src指定可以提供网络字体的起源。Google的Web字体可以通过启用font-src https://themes.googleusercontent.com
- form-action列出从
- frame-ancestors指定可以嵌入当前页面的源代码。该指令适用于,
- frame-src弃用。使用child-src来代替。
- img-src 定义可以加载图像的起源。
- media-src 限制允许传送视频和音频的起源。
- object-src 允许控制Flash和其他插件。
- plugin-types 限制页面可能调用的插件种类。
- report-uri指定浏览器在违反内容安全策略时发送报告的URL。此指令不能在标签中使用。
- style-src是script-src样式表的对应物。
- upgrade-insecure-requests指示用户代理重写URL方案,将HTTP更改为HTTPS。此指令适用于需要重写的大量旧URL的网站。
默认情况下,指令是广泛的。如果您没有为指令设置特定的策略font-src,那么那么该指令的默认行为就像您指定*为有效的源一样(例如,您可以无限制地加载来自各地的字体)。
您可以通过指定一个default-src指令来覆盖此默认行为。您可能怀疑,该指令定义了您未指定的大多数指令的默认值。一般来说,这适用于以任何指令为结尾-src。如果default-src设置为https://example.com,并且您无法指定font-src指令,则可以从而https://example.com不是其他地方加载字体。我们仅script-src在我们早期的例子中指定,这意味着可以从任何来源加载图像,字体等。
以下指令不使用default-src作为备用。记住,不能设置它们与允许任何东西是一样的。
- base-uri
- form-action
- frame-ancestors
- plugin-types
- report-uri
- sandbox
您可以使用尽可能少的这些指令对您的特定应用程序是有意义的,只需在HTTP标头中列出每个指令,并使用分号分隔指令。您需要确保在单个指令中列出特定类型的所有必需资源。如果写了一些东西
script-src https://host1.com; script-src https://host2.com
第二个指令将被忽略。类似以下内容将正确地将两个起始位置指定为有效。
script-src https://host1.com https://host2.com
例如,如果您有一个应用程序从内容传送网络(例如https://cdn.example.net)加载其所有资源,并且知道您根本不需要框架内容或任何插件,那么您的策略可能如下所示:
Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'
实施细节
您将在网络上的各种教程中看到X-WebKit-CSP和X-Content-Security-Policy标题。展望未来,您可以忽略这些前缀标题。现代浏览器(IE除外)支持未标记的Content-Security-Policy标题。这是你应该使用的标题。
不管您使用的标头,策略是逐页定义的:您需要发送HTTP标头以及您希望确保受保护的每个响应。这提供了很多灵活性,因为您可以根据具体需求对特定页面的策略进行微调。您网站中的一组网页也许有一个+1按钮,而另外一些则不需要:只有在必要时才可以加载按钮代码。
每个指令中的源列表是相当灵活的。您可以通过scheme(data:,https:)指定源,或者以hostname-only(example.com与该主机上的任何源相匹配的任何方案,任何端口)与完全限定的URI(https://example.com:443仅匹配HTTPS,仅example.com与唯一端口443)。通配符被接受,但仅作为方案,端口或主机名的最左侧位置:://.example.com:*将匹配任何端口上使用任何方案的example.com(但不是 example.com其本身)的所有子域。
源列表中也接受了四个关键字:
- 'none',你可能会期望,什么也没有。
- 'self' 匹配当前来源,但不匹配其子域。
- 'unsafe-inline' 允许内联JavaScript和CSS(稍后会详细介绍一下)。
- 'unsafe-eval'允许文本到JavaScript的机制eval(我们也会得到这个)。
这些关键字需要单引号。script-src 'self'(带引号)授权从当前主机执行JavaScript。script-src self(没有引号)允许来自名为“ self”(而不是当前主机)的服务器的JavaScript ,这可能不是您的意思。
沙箱
还有一个更直接的值得谈论:sandbox。这与我们所看到的其他人有所不同,而且页面可以采取的行动限制,而不是页面可以加载的资源。如果sandbox指令存在,因为虽然它被装载的内页将被视为iframe同一个sandbox属性。这可以在页面上产生广泛的影响:强制页面成为一个独特的起源,并防止表单提交等。这超出了本文的范围,但是您可以在HTML5规范的“沙盒标志集”部分找到有效沙箱属性的完整详细信息。
元标记
CSP首选的传递机制是一个HTTP头。但是,可以直接在标记中在页面上设置策略。使用具有http-equiv属性的元标记:
这不能用于frame-ancestors,report-uri或sandbox。
内联代码被认为有害
应该清楚的是,CSP基于白名单,因为这是一个明确的方式来指示浏览器将特定的资源集合视为可接受的,并拒绝其余的。但是,基于Origin的白名单并不能解决XSS攻击造成的最大威胁:内嵌脚本注入。如果攻击者可以注入直接包含一些恶意的有效载荷()的脚本标签,则浏览器没有任何机制可以将其与合法的内联脚本标记区分开来。CSP通过完全禁止内联脚本来解决这个问题:这是唯一可以确定的方法。
此禁令不仅包括直接嵌入到script标签中的脚本,还包括内联事件处理程序和javascript:URL。您需要将script标签的内容移动到外部文件中,并替换javascript:URL和适当的addEventListener调用。例如,您可以从以下内容重写以下内容:
更像是:
// amazing.js function doAmazingThings() { alert('YOU AM AMAZING!'); } document.addEventListener('DOMContentReady', function () { document.getElementById('amazing') .addEventListener('click', doAmazingThings); });
重写的代码具有上述优点,并且与CSP无关的许多优点; 这是最好的做法,不管你使用CSP。内联JavaScript将结构和行为整合到不应该的方式。浏览器缓存外部资源更容易,开发人员更易于理解,有利于编译和分类。如果您将代码移植到外部资源中,您将编写更好的代码。
内联样式以相同的方式处理:style属性和style标签都应整合到外部样式表中,以防止CSS启用的各种令人惊奇的巧妙数据exfiltration方法。
如果你真的,绝对必须有内联的脚本和风格,你可以通过'unsafe-inline'在一个script-src或者style-src指令中添加一个允许的源来启用它。您也可以使用随机数或有(见下文)。但请不要。禁止内联脚本是CSP提供的最大的安全胜利,禁止内联样式也会使您的应用程序更加硬化。在将所有代码移出所有代码之后,确保事情正常运行,这是一个很大的努力,但这是一个非常值得的折中。
如果你绝对必须使用它...
CSP Level 2通过允许您使用加密随机数(使用一次数)或散列值将特定内联脚本列入白名单,为内联脚本提供向后兼容性。虽然在实践中这可能很麻烦,但它是有用的。
要使用随机数,请将脚本标签赋给一个nonce属性。其值必须与受信任来源列表中的值相匹配。例如:
现在,将nonce添加到附加到nonce-关键字的script-src指令中。
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
请记住,必须为每个页面请求重新生成随机数,并且它们必须是无法确定的。
哈希的工作方式大致相同。不要在脚本标签中添加代码,而是创建脚本本身的SHA哈希值,并将其添加到script-src指令中。例如,假设您的页面包含以下内容:
您的政策将包含以下内容:
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
这里有几件事要注意。该sha*-前缀指定用于生成哈希的算法。在上面的例子中,使用sha256-。CSP还支持sha384-和sha512-。当生成哈希时不包括