内容安全策略 (CSP) 是如何工作的?

355

我在开发者控制台中遇到了一堆错误:

拒绝评估字符串

拒绝执行内联脚本,因为它违反了以下内容安全策略指令

拒绝加载脚本

拒绝加载样式表

这是怎么回事?内容安全策略(CSP)是如何工作的?我该如何使用 Content-Security-Policy HTTP 头?

具体来说,如何...

  1. ...允许多个来源?
  2. ...使用不同的指令?
  3. ...使用多个指令?
  4. ...处理端口?
  5. ...处理不同的协议?
  6. ...允许 file:// 协议?
  7. ...使用内联样式、脚本和标签 <style><script>
  8. ...允许 eval()

最后:

  1. 'self' 究竟是什么意思?

4
https://content-security-policy.com - sites
2个回答

817
标签中的Content-Security-Policy允许您定义资源可以从哪里加载,防止浏览器从任何其他位置加载数据,从而降低跨站脚本攻击风险。这使得攻击者更难将恶意代码注入到您的站点中。为了简洁起见,示例中我不会在每个样本中写完整的标记。相反,我只会展示其content属性。因此,例如content="default-src 'self'"意味着:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">

1. 如何允许多个来源?

您可以在指令后简单地列出源列表,并以空格分隔:

content="default-src 'self' https://example.com/js/"

请注意,除了像'self'这样的特殊参数之外,其他参数周围没有引号。指令后面也没有冒号(:)。只需指令,然后是以空格分隔的参数列表。

指定参数以下的所有内容都会被隐式允许。这意味着在上面的示例中,以下内容都将被视为有效来源:

https://example.com/js/file.js
https://example.com/js/subdir/anotherfile.js

然而,这些是无效的:

http://example.com/js/file.js
^^^^ wrong protocol

https://example.com/file.js
                   ^^ above the specified path

2. 我该如何使用不同的指令?它们各自是用来做什么的?

最常见的指令包括:

  • default-src 用于加载 JavaScript、图片、CSS、字体、AJAX请求等资源的默认策略
  • script-src 定义 JavaScript 文件的有效来源
  • style-src 定义 CSS 文件的有效来源
  • img-src 定义图像的有效来源
  • connect-src 定义 XMLHttpRequest(AJAX)、WebSockets 或 EventSource 的有效目标。如果尝试连接到未在此处允许的主机,则浏览器将模拟一个 400 错误。

还有其他指令,但这些是你最有可能需要的。

3. 我该如何使用多个指令?

你可以在一个 meta 标签中定义所有的指令,并通过分号(;)来终止它们:

content="default-src 'self' https://example.com/js/; style-src 'self'"

4. 如何处理端口?

除了默认端口外,其他端口需要通过添加端口号或允许域名后面加星号来显式允许。

content="default-src 'self' https://ajax.googleapis.com http://example.com:123/free/stuff/"

上述操作将产生以下结果:

https://ajax.googleapis.com:123
                           ^^^^ Not ok, wrong port

https://ajax.googleapis.com - OK

http://example.com/free/stuff/file.js
                 ^^ Not ok, only the port 123 is allowed

http://example.com:123/free/stuff/file.js - OK

正如我所提到的,您还可以使用星号(*)来明确允许所有端口:

content="default-src example.com:*"

5. 我该如何处理不同的协议?

默认情况下,只允许使用标准协议。例如,要允许WebSocket ws://,您必须明确允许它:

content="default-src 'self'; connect-src ws:; style-src 'self'"
                                         ^^^ web Sockets are now allowed on all domains and ports.

6. 如何允许使用文件协议 file://

如果您将其定义为这样,它将不起作用。相反,您需要使用filesystem参数来允许它:

content="default-src filesystem"

7. 如何使用内联脚本和样式定义?

除非明确允许,否则您不能使用内联样式定义、<script>标签中的代码或标记属性中的代码(如onclick)。您可以像这样允许它们:

content="script-src 'unsafe-inline'; style-src 'unsafe-inline'"

您还需要明确允许内联的 base64 编码图片:

content="img-src data:"

8. 如何允许使用eval()

我相信很多人会说你不需要,因为“eval就是邪恶”的,很可能是世界末日的主要原因。但这些人错了。当然,使用eval确实可能给您的网站安全带来重大漏洞,但它具有完全有效的用例。您只需要在使用时明智一些即可。可以按以下方式允许其运行:

content="script-src 'unsafe-eval'"

9. 'self'的确切含义是什么?

你可能认为'self'指代本地主机、本地文件系统或同一主机上的任何内容,但它并不是这些。它指的是与定义内容策略文件相同方案(协议)、同一主机和同一端口的源。你的网站使用HTTP进行服务吗?那就不能使用HTTPS,除非你明确定义了它。

我在大多数示例中使用了'self',因为通常包括它是有意义的,但这并不是必须的。如果不需要它,就可以将其省略掉。

但等等!我难道不能只使用content="default-src *"就搞定了吗?

不行。除了明显的安全漏洞外,这也无法按照你预期的工作。虽然一些文档声称它允许所有内容,但事实并非如此。它不允许内嵌或评估,因此要使你的网站真正变得极度脆弱,你应该使用以下代码:

content="default-src * 'unsafe-inline' 'unsafe-eval'"

......但我相信你不会这样做。

进一步阅读:

http://content-security-policy.com

http://en.wikipedia.org/wiki/Content_Security_Policy


6
很棒的帖子。有一个问题:当指定多个指令时会发生什么并不明显;例如3中的style-src设置是否优先于default-src?等等... - track0
53
因此,为了允许所有内容,内容应为default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src * 'unsafe-inline'; frame-src *; - Arnold Roa
12
了解content="default-src * 'unsafe-inline' 'unsafe-eval'"的重要性,对于使一些Angular应用程序正常工作非常必要。 - flanger001
5
@Mahesh那个“博客”中充满了从stackoverflow(SO)复制的文章。似乎不太可能有这么多SO用户会复制一个不知名博主的内容 - 我知道我没有这样做过。 - Schlaus
7
关于 connect-src 和路径的简短说明:如果你想包含整个子路径,尾部斜杠是必须的。例如:如果源是 http://foo.com/files,文件 http://foo.com/files/bar.txt 将被阻止,但是如果源是 http://foo.com/files/,该文件将被提供。 - Gerrit-K
显示剩余23条评论

21

Apache 2 mod_headers

您还可以启用 Apache 2 mod_headers。在 Fedora 上,默认已启用。如果您使用的是 Ubuntu/Debian,请按照以下方式启用:

# First enable headers module for Apache 2,
# and then restart the Apache2 service
a2enmod headers
apache2 -k graceful

在Ubuntu/Debian中,您可以在文件/etc/apache2/conf-enabled/security.conf中配置头信息。
#
# Setting this header will prevent MSIE from interpreting files as something
# else than declared by the content type in the HTTP headers.
# Requires mod_headers to be enabled.
#
#Header set X-Content-Type-Options: "nosniff"

#
# Setting this header will prevent other sites from embedding pages from this
# site as frames. This defends against clickjacking attacks.
# Requires mod_headers to be enabled.
#
Header always set X-Frame-Options: "sameorigin"
Header always set X-Content-Type-Options nosniff
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Permitted-Cross-Domain-Policies "master-only"
Header always set Cache-Control "no-cache, no-store, must-revalidate"
Header always set Pragma "no-cache"
Header always set Expires "-1"
Header always set Content-Security-Policy: "default-src 'none';"
Header always set Content-Security-Policy: "script-src 'self' www.google-analytics.com adserver.example.com www.example.com;"
Header always set Content-Security-Policy: "style-src 'self' www.example.com;"

注意:这是文件的底部。只有最后三个条目是CSP设置。

第一个参数是指令,第二个参数是要列入白名单的源。我已经添加了Google分析和广告服务器,您可能也会用到。此外,如果您在Apache 2中配置了别名,例如www.example.com和example.com,则也应将它们添加到白名单中。

内联代码被认为是有害的,应该避免使用。将所有JavaScript代码和CSS复制到单独的文件中,并将它们加入到白名单中。

当你在处理它时,你可以查看其他头设置并安装mod_security

更多阅读:

https://developers.google.com/web/fundamentals/security/csp/

https://www.w3.org/TR/CSP/


2
我能够将这些指令添加到我的 .htaccess 文件中,因为我没有在共享主机上编辑 Apache 配置的能力。我在 https://report-uri.io/home/tools 找到了优秀的工具来调整这些设置。 - Michael McGinnis
有没有办法在Tomcat 7中解决这个问题。我尝试过添加过滤器,但并没有起作用。 - Elshan
如果我的应用程序通过Jboss强制执行CSP,但与未设置CSP头的应用程序进行通信,那么我的应用程序是否会因为第三方应用程序而存在漏洞?我该如何减轻这种风险? - Bionix1441

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接