内容安全策略(CSP),防御 XSS 攻击的好助手

什么是 CSP?

其核心思想十分简单:网站通过发送一个 CSP 头部,来告诉浏览器什么是被授权执行的与什么是需要被禁止的。

这里有一个 PHP 的例子:


  1. <?php
  2. header("Content-Security-Policy: <your directives>");
  3. ?>

一些指令

你可以定义一些全局规则或者定义一些涉及某一类资源的规则:


  1. default-src 'self' ;
  2. # self = 同端口,同域名,同协议 => 允许

基础参数是 default-src:如果没有为某一类资源设置指令规则,那么浏览器就会使用这个默认参数值。


  1. script-src 'self' www.google-analytics.com ;
  2. # 来自这些域名的 JS 文件 => 允许

在这个例子中,我们已经授权了 www.google-analytics.com 这个域名来源的 JavaScript 文件使用到我们的网站上。我们也添加了 'self' 这个关键词;如果我们通过 script-src 来重新设置其它的规则指令,它将会覆盖 default-src 规则。

如果没有指明协议(scheme)或端口,它就会强制选择与当前页面相同的协议或端口。这样做防止了混合内容(LCTT 译注:混合内容指 HTTPS 页面中也有非 HTTPS 资源,可参见: https://developer.mozilla.org/zh-CN/docs/Security/MixedContent )。如果页面是 https://example.com,那么你将无法加载 http://www.google-analytics.com/file.js 因为它已经被禁止了(协议不匹配)。然而,有一个例外就是协议的提升是被允许的。如果 http://example.com 尝试加载 https://www.google-analytics.com/file.js,接着协议或端口允许被更改以便协议的提升。


  1. style-src 'self' data: ;
  2. # Data-Uri 嵌入 CSS => 允许

在这个例子中,关键词 data: 授权了在 CSS 文件中 data 内嵌内容。

在 CSP 1 规范下,你也可以设置如下规则:

  • img-src 有效的图片来源
  • connect-src 应用于 XMLHttpRequest(AJAX),WebSocket 或 EventSource
  • font-src 有效的字体来源
  • object-src 有效的插件来源(例如,<object><embed><applet>
  • media-src 有效的 <audio> 和 <video> 来源

CSP 2 规范包含了如下规则:

  • child-src 有效的 web workers 和 元素来源,如 <frame> 和 <iframe> (这个指令用来替代 CSP 1 中废弃了的 frame-src 指令)
  • form-action 可以作为 HTML <form> 的 action 的有效来源
  • frame-ancestors 使用 <frame><iframe><object><embed> 或 <applet> 内嵌资源的有效来源
  • upgrade-insecure-requests 命令用户代理来重写 URL 协议,将 HTTP 改到 HTTPS (为一些需要重写大量陈旧 URL 的网站提供了方便)。

为了更好的向后兼容一些废弃的属性,你可以简单的复制当前指令的内容同时为那个废弃的指令创建一个相同的副本。例如,你可以复制 child-src 的内容同时在 frame-src 中添加一份相同的副本。

CSP 2 允许你添加路径到白名单中(CSP 1 只允许域名被添加到白名单中)。因此,相较于将整个 www.foo.com 域添加到白名单,你可以通过添加 www.foo.com/some/folder 这样的路径到白名单中来作更多的限制。这个需要浏览器中 CSP 2 的支持,但它很明显更安全。

一个例子

我为 Web 2015 巴黎大会上我的演讲 “CSP in Action”制作了一个简单的例子。

在没有 CSP 的情况下,页面展示如下图所示:

不是十分优美。要是我们启用了如下的 CSP 指令又会怎样呢?


  1. <?php
  2. header("Content-Security-Policy:
  3. default-src 'self' ;
  4. script-src 'self' www.google-analytics.com stats.g.doubleclick.net ;
  5. style-src 'self' data: ;
  6. img-src 'self' www.google-analytics.com stats.g.doubleclick.net data: ;
  7. frame-src 'self' ;");
  8. ?>

浏览器将会作什么呢?它会(非常严格的)在 CSP 基础规则之下应用这些指令,这意味着任何没有在 CSP 指令中被授权允许的都将会被禁止(“blocked” 指的是不被执行、不被显示并且不被使用在网站中)。

在 CSP 的默认设置中,内联脚本和样式是不被授权的,意味着每一个 <script>onclick 事件属性或style 属性都将会被禁止。你可以使用 style-src 'unsafe-inline' ; 指令来授权使用内联 CSS。

在一个支持 CSP 的现代浏览器中,上述示例看起来如下图:

发生了什么?浏览器应用了指令并且拒绝了所有没有被授权的内容。它在浏览器调试终端中发送了这些通知:

如果你依然不确定 CSP 的价值,请看一下 Aaron Gustafson 文章 “More Proof We Don't Control Our Web Pages”。

当然,你可以使用比我们在示例中提供的更严格的指令:

  • 设置 default-src 为 'none'
  • 为每条规则指定你的设置
  • 为请求的文件指定它的绝对路径

更多关于 CSP 的信息

支持

CSP 不是一个需要复杂的配置才能正常工作的每日构建特性。CSP 1 和 2 是候选推荐标准!浏览器可以非常完美的支持 CSP 1

CSP 2 是较新的规范,因此对它的支持会少那么一点。

现在 CSP 3 还是一个早期草案,因此还没有被支持,但是你依然可以使用 CSP 1 和 2 来做一些重大的事。

其他需要考虑的因素

CSP 被设计用来降低跨站脚本攻击(XSS)的风险,这就是不建议开启内联脚本和 script-src 指令的原因。Firefox 对这个问题做了很好的说明:在浏览器中,敲击 Shift + F2 并且键入 security csp,它就会向你展示指令和对应的建议。这里有一个在 Twitter 网站中应用的例子:

如果你确实需要使用内联脚本和样式的话,另一种可能就是生成一份散列值。例如,我们假定你需要使用如下的内联脚本:


  1. <script>alert('Hello, world.');</script>

你应该在 script-src 指令中添加 sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng= 作为有效来源。这个散列值用下面的 PHP 脚本执行获得的结果:


  1. <?php
  2. echo base64_encode(hash('sha256', "alert('Hello, world.');", true));
  3. ?>

我在前文中说过 CSP 被设计用来降低 XSS 风险,我还得加上“……与降低未经请求内容的风险。”伴随着 CSP 的使用,你必须知道你内容的来源是哪里与它们在你的前端都作了些什么(内联样式,等)。CSP 同时可以帮助你让贡献者、开发人员和其他人员来遵循你内容来源的规则!

现在你的问题就只是,“不错,这很好,但是我们如何在生产环境中使用它呢?”

如何在现实世界中使用它

想要在第一次使用 CSP 之后就失望透顶的方法就是在生产环境中测试。不要想当然的认为,“这会很简单。我的代码是完美并且相当清晰的。”不要这样作。我这样干过。相信我,这相当的蠢。

正如我之前说明的,CSP 指令由 CSP 头部来激活,这中间没有过渡阶段。你恰恰就是其中的薄弱环节。你可能会忘记授权某些东西或者遗忘了你网站中的一小段代码。CSP 不会饶恕你的疏忽。然而,CSP 的两个特性将这个问题变得相当的简单。

report-uri

还记得 CSP 发送到终端中的那些通知么?report-uri 指令可以被用来告诉浏览器发送那些通知到指定的地址。报告以 JSON 格式送出。


  1. report-uri /csp-parser.php ;

因此,我们可以在 csp-parser.php 文件中处理有浏览器送出的数据。这里有一个由 PHP 实现的最基础的例子:


  1. $data = file_get_contents('php://input');
  2. if ($data = json_decode($data, true)) {
  3. $data = json_encode(
  4. $data,
  5. JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
  6. );
  7. mail(EMAIL, SUBJECT, $data);
  8. }

这个通知将会被转换成为一封邮件。在开发过程中,你可能不会需要比这更复杂的其它东西。

对于一个生产环境(或者是一个有较多访问的开发环境),你应该使用一种比邮件更好的收集信息的方式,因为这种方式在节点上没有验证和速率限制,并且 CSP 可能变得乱哄哄的。只需想像一个会产生 100 个 CSP 通知(例如,一个从未授权来源展示图片的脚本)并且每天会被浏览 100 次的页面,那你就会每天收到 10000 个通知啊!

例如 report-uri.io 这样的服务可以用来简化你的通知管理。你也可以在 GitHub上看一些另外的使用report-uri (与数据库搭配,添加一些优化,等)的简单例子。

report-only

正如我们所见的,最大的问题就是在使用和不使用 CSP 之间没有中间地带。然而,一个名为 report-only 的特性会发送一个稍有不同的头部:


  1. <?php
  2. header("Content-Security-Policy-Report-Only: <your directives>");
  3. ?>

总的来说,这个头部就是告诉浏览器,“表现得似乎所有的 CSP 指令都被应用了,但是不禁止任何东西。只是发送通知给自己。”这是一种相当棒的测试指令的方式,避免了任何有价值的东西被禁止的风险。

在 report-only 和 report-uri 的帮助下你可以毫无风险的测试 CSP 指令,并且可以实时的监控网站上一切与 CSP 相关的内容。这两个特性对部署和维护 CSP 来说真是相当的有用!

结论

为什么 CSP 很酷

CSP 对你的用户来说是尤其重要的:他们在你的网站上不再需要遭受任何的未经请求的脚本,内容或 XSS 的威胁了。

对于网站维护者来说 CSP 最重要的优势就是可感知。如果你对图片来源设置了严格的规则,这时一个脚本小子尝试在你的网站上插入一张未授权来源的图片,那么这张图片就会被禁止,并且你会在第一时间收到提醒。

开发者也需要确切的知道他们的前端代码都在做些什么,CSP 可以帮助他们掌控一切。会促使他们去重构他们代码中的某些部分(避免内联函数和样式,等)并且促使他们遵循最佳实践。

如何让 CSP 变得更酷

讽刺的是,CSP 在一些浏览器中过分的高效了,在和书签栏小程序一起使用时会产生一些 bug。因此,不要更新你的 CSP 指令来允许书签栏小程序。我们无法单独的责备任何一个浏览器;它们都有些问题:

  • Firefox
  • Chrome (Blink)
  • WebKit

大多数情况下,这些 bug 都是禁止通知中的误报。所有的浏览器提供者都在努力解决这些问题,因此我们可以期待很快就会被解决。无论怎样,这都不会成为你使用 CSP 的绊脚石。

原文发布时间为:2016-10-10

本文来自合作伙伴“Linux中国”

时间: 2024-09-11 13:53:14

内容安全策略(CSP),防御 XSS 攻击的好助手的相关文章

Web 安全之内容安全策略 (CSP)

内容安全策略 (CSP, Content Security Policy) 是一个附加的安全层,用于帮助检测和缓解某些类型的攻击,包括跨站脚本攻击 (XSS) 和数据注入等攻击. 这些攻击可用于实现从数据窃取到网站破坏或作为恶意软件分发版本等用途.内容安全策略在现代浏览器中已经包含,使用的是 W3C CSP 1.0 标准中描述的 Content-Security-Policy 头部和指令. 那么如何应用? CSP 可以由两种方式指定:HTTP Header 和 HTML.HTTP 是在 HTTP

浏览器安全策略说之内容安全策略CSP(1)

前言2013年11月Veracode给出的报告指出,全球前1000000网站中仅有269个网站使用了W3C规范的CSP策略头Content-Security-Policy.而在2014年2月ZoomEye给出的测试报告中,国内排名前7000的域名没有使用CSP,国内1千万的域名(含子域名)中仅发现7个使用了CSP策略,其中还有3个网站CSP语法使用错误.如果说CSP是一个伟大的安全策略,为何全球范围内网站使用率如此之低?是CSP自身的设计存在问题,还是网站管理员们没有去充分了解和利用它.CSP到

如何利用HttpOnly来防御xss攻击

xss的概念就不用多说了,它的危害是极大的,这就意味着一旦你的网站出现xss漏洞,就可以执行任意的js代码,最可怕的是攻击者利用js获取cookie或者session劫持,如果这里面包含了大量敏感信息(身份信息,管理员信息)等,那完了... 如下js获取cookie信息: url=document.top.location.href; cookie=document.cookie; c=new Image(); c.src='http://www.******.com/c.php?c='+coo

防御XSS攻击的七条原则

前言 本文将会着重介绍防御XSS攻击的一些原则,需要读者对于XSS有所了解,至少知道XSS漏洞的基本原理,如果您对此不是特别清楚,请参考这两篇文章:<Stored and Reflected XSS Attack><DOM Based XSS> 攻击者可以利用XSS漏洞向用户发送攻击脚本,而用户的浏览器因为没有办法知道这段脚本是不可信的,所以依然会执行它.对于浏览器而言,它认为这段脚本是来自可以信任的服务器的,所以脚本可以光明正大地访问Cookie,或者保存在浏览器里被当前网站所用

Jsoup代码解读之八-防御XSS攻击

防御XSS攻击的一般原理 cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入中的XSS防御. 我们知道,XSS攻击的一般方式是,通过在页面输入中嵌入一段恶意脚本,对输出时的DOM结构进行修改,从而达到执行这段脚本的目的.对于纯文本输入,过滤/转义HTML特殊字符<,>,",'是行之有效的办法,但是如果本身http://www.aliyun.com/zixun/aggregation/18678.html">用户输入的就是一段HTML文本(例如博客文章

使用脚本隔离技术对抗XSS攻击

本文讲的是使用脚本隔离技术对抗XSS攻击, 概述 在圣诞节之后,我与@mikewest和@fgrx讨论了关于使用"隔离"的概念(将会在下面解释)作为对抗XSS漏洞攻击的安全缓解措施.这看起来似乎是一个很有趣的问题,因此,我花了一些时间研究了一下. 下面描述的设计方案将允许你(web开发人员)通过两个简单的步骤保护一些客户端免受XSS攻击: 设置新的cookie标志(isolatedScript = true) 设置一个HTTP标头(Isolated-Script:true) 就这样.有

防御XSS的七条原则

 攻击者可以利用XSS漏洞向用户发送攻击脚本,而用户的浏览器因为没有办法知道这段脚本是不可信的,所以依然会执行它.对于浏览器而言,它认为这段脚本是来自可以信任的服务器的,所以脚本可以光明正大地访问Cookie,或者保存在浏览器里被当前网站所用的敏感信息,甚至可以知道用户电脑安装了哪些软件.这些脚本还可以改写HTML页面,进行钓鱼攻击. 虽然产生XSS漏洞的原因各种各样,对于漏洞的利用也是花样百出,但是如果我们遵循本文提到防御原则,我们依然可以做到防止XSS攻击的发生. 有人可能会问,防御XSS的

XSS攻击的解决方法

在我上一篇<前端安全之XSS攻击>文中,并没有把XSS攻击的解决办法说完整,而XSS的攻击又那么五花八门,有没有一招"独孤九剑"能够抗衡,毕竟那么多情况场景,开发人员无法一一照顾过来,而今天通过阅读<白帽子讲Web安全>这本书,对应对方式有了更好的总结,分为两类,一是服务端可以干的事,二是客户端可以干的事. 前提 在说XSS解决方式时,有一个前提.就是同源策略--浏览器的同源策略(浏览器安全的基础,即使是攻击脚本也要遵守这法则),限制了来自不同源的"d

世界第一个XSS攻击蠕虫的原理

Kamkar近日在Github上发布了 一款软件并附上指导教程,教你如何修改无人机设置,使之认证失效并对其进行攻击.该Perl软件名为SkyJack,运行在 Raspberry Pi上并使用其它开源软件来劫持飞行器.这一新闻让我对此人非常敬佩,翻译了他关于如何在MySpace上实现第一个XSS工具蠕虫代码的说明,翻译的过程也是对XSS攻击的一次学习过程和对黑客精神的震撼.1)Myspace 屏蔽了很多标志符.事实上,他们只允许<a>,<img>类,和<div>类,或许还