CSP 内容安全策略
一, CSP (内容安全策略)介绍
CSP(内容安全策略)
是一种额外的安全层,可以帮助检测和减轻某些类型的攻击,如跨站脚本攻击(XSS)和数据注入攻击。
CSP允许网站管理员定义哪些动态资源允许执行和加载,有助于防止恶意脚本执行以及非授权内容的加载。
通过使用Content-Security-Policy
HTTP头部,网站可以控制用户代理如何执行页面的特定部分,这可以显著减少XSS攻击的风险。CSP的一些常见指令包括:
py
default-src:设置默认加载资源的策略(例如,自身的源、任何地方的源、或者不加载任何外部资源)。
script-src:定义哪些脚本可以执行, 例如script标签, a标签的JavaScript:location.href="" 等.
style-src:定义哪些样式表可以加载。
img-src:定义哪些图片资源可以加载。
connect-src:限制可以通过脚本接口进行连接的URL(例如,AJAX 请求、WebSocket)。
font-src:定义哪些字体资源可以加载。
object-src:限制可以加载哪些插件。
media-src:定义哪些媒体资源(音频和视频)可以加载。
frame-src:定义哪些iframe可以加载。
二, 配置 CSP 策略
1. 方式一: 设置响应头
php
<?php
// 只允许加载来自同源的所有资源
header("Content-Security-Policy: script-src 'self'");
// 允许执行来自同源以及http://192.168.112.183的脚本。
header("Content-Security-Policy: script-src 'self' http://192.168.112.183");
// 允许执行来自同源以及http://http://192.168.112.183的行内脚本
// 比如 <a javascript:location.href="http://192.168.112.183/hack.php"></a>
header("Content-Security-Policy: script-src 'self' http://192.168.112.183 'unsafe-inline'");
2. 方式二: 设置meta
标签
虽然通过服务器发送HTTP响应头是最常见的实现方式,但你也可以直接在HTML文件中的标签中指定CSP。这种方法通常用于单个HTML页面的情况,或者在无法控制服务器响应头的环境中(例如某些静态站点托管服务)。
html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://apis.example.com; img-src 'self' https://images.example.com; style-src 'self' 'unsafe-inline';">
<title>My Secure Page</title>
</head>
<body>
<!-- 页面内容 -->
</body>
</html>
在上面的例子中,<meta>
标签使用了http-equiv属性来模拟一个HTTP响应头。
content
属性中定义了CSP规则,与通过HTTP头发送的规则相同。
然而,需要注意的是,使用<meta>
标签设置CSP的方法存在一些限制:
py
1. 无法使用某些指令,如frame-ancestors和report-uri。
2. 对于由多个页面组成的网站,每个页面都需要包含一个<meta>标签,这可能会导致维护上的困难。
3. 与通过HTTP头设置相比,这种方法可能会更容易遭受某些攻击方式,
因为恶意用户可能会尝试注入标签来覆盖你的CSP规则。
总的来说,虽然在HTML中设置CSP是可能的,但出于维护和安全性的考虑,通常建议通过HTTP头来设置CSP。
3. 方式三: 全局配置CSP, 修改服务器配置文件
除了在PHP脚本或HTML中直接设置CSP之外,还可以在Web服务器的全局配置中设置内容安全策略(CSP)。
这样做的好处是,你可以为所有服务的页面统一定义安全策略,而不必为每个单独的页面或脚本设置。
以下是在几种常用Web服务器中设置CSP的方法:
Apache
在Apache中,你可以在 .htaccess 文件、虚拟主机配置文件,或者直接在全局配置文件 httpd.conf 中使用 Header 指令来设置CSP。例如:
py
<IfModule mod_headers.c>
Header set Content-Security-Policy "default-src 'self';"
</IfModule>
Nginx
在Nginx中,你可以在服务器配置块中添加 add_header 指令来设置CSP,如下所示:
py
server {
add_header Content-Security-Policy "default-src 'self';";
...
}
Microsoft IIS
在Microsoft IIS中,你可以通过Web.config文件来设置HTTP响应头,类似于这样:
py
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy" value="default-src 'self';" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
如果你使用的是CDN(内容分发网络)
或者代理服务(如Cloudflare)
,这些服务通常提供了方便的界面来设置响应头,包括CSP。
这样你可以在不直接操作服务器配置的情况下,全局地管理这些策略。
在所有情况下,更改全局配置文件时都应该非常谨慎,因为错误的配置可能导致服务中断。
更改后,务必重启服务并测试确保配置正确生效。
此外,全局设置CSP可能会影响网站的所有部分,所以在启用前,需要确保所有部分都符合CSP的规定,以免意外破坏网站的功能。
三, 配置 CSP 安全报告
1. 拦截并报告CSP违规.
添加 report-uri
指令:
php
<?php
header("Content-Security-Policy: default-src 'self'; report-uri /csp-violation-report-endpoint.php");
?>
在这个例子中,当CSP违规发生时,浏览器会向 csp-violation-report-endpoint.php
发送json
格式的报告。
你需要确保你的服务器端准备好接收和处理这些报告,通常是通过一个PHP脚本来记录这些违规。
php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 这里发送过来的json没有key, 不能用$_POST[key]取值, 用伪协议获取内容
$data = file_get_contents('php://input');
// 这里可以将$data写入数据库,或者记录到日志文件等。
file_put_contents('csp-violations.log', $data, FILE_APPEND);
// 显示报告保存成功的消息
header("Content-Type: application/json");
echo json_encode(['success' => true]);
} else {
header("HTTP/1.1 405 Method Not Allowed");
echo "You must use POST request to send CSP reports.";
}
?>
在实际部署CSP策略之前,在开发和测试环境中充分测试这些策略是非常重要的。
这样可以确保CSP设置不会意外破坏网站的正常功能。
同时,对于复杂的网站,逐步实施CSP可能是更安全的做法,可以首先在报告模式下启动,然后再逐步转换到强制模式。
2. 仅报告而不拦截的CSP.
可以使用 Content-Security-Policy-Report-Only
头部,而不是 Content-Security-Policy
。
这个头部的格式与常规的CSP相同,但只用于报告目的。
php
header("Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-violation-report-endpoint.php");
在这种模式下,如果有脚本尝试从其他源加载,这个行为不会被浏览器阻止,但会向指定的URI发送报告。
四, CSP实战绕过
注入代码:
html
<script>alert(111)</script>
1. 低级防御
(1). 控制台警告:
py
GET
http://192.168.112.200/DVWA-master/vulnerabilities/csp/<script>alert(1)<script>
[HTTP/1.1 404 Not Found 6ms]
指向"http://192.168.112.200/DVWA-master/vulnerabilities/csp/%3Cscript%3Ealert(1)%3Cscript%3E"
的 <script> 加载失败。 csp:62:40
这里发现script标签没作用.
再看一下前端的代码:
html
<script src="<script>alert(111)</script>"></script>
这里看到注入的代码被写到了src的位置, 那么就知道了这里实际需要注入的是一个js脚本的地址.
(2). 查看响应头:
py
HTTP/1.1 200 OK
Date: Mon, 06 Nov 2023 09:30:30 GMT
Server: Apache/2.4.48 (Unix) OpenSSL/1.1.1k PHP/7.3.29 mod_perl/2.0.11 Perl/v5.32.1
X-Powered-By: PHP/7.3.29
Expires: Tue, 23 Jun 2009 12:00:00 GMT
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Content-Security-Policy: script-src 'self' https://pastebin.com hastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;
Content-Length: 4144
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html;charset=utf-8
(3). 分析响应头
这里可以看到后端设置了CSP策略:
py
Content-Security-Policy: script-src 'self'
https://pastebin.com hastebin.com example.com code.jquery.com
https://ssl.google-analytics.com ;
'self':
表示允许加载执行与加载文档处于同一源的脚本(例如,与你的网页相同的域名)。
https://pastebin.com:
允许加载执行来自 https://pastebin.com 的脚本。
hastebin.com:
由于没有指定协议(如 http:// 或 https://),这将默认允许使用与包含文档相同的协议的 hastebin.com 域下的脚本。如果你的网页是通过 HTTPS 服务的,那么它将会是 https://hastebin.com。
example.com:
同样,这没有指定协议,因此它也会默认允许与你的网页相同协议的 example.com 下的脚本。
code.jquery.com:
这会允许加载执行来自 code.jquery.com 的脚本,同样适用于与包含文档相同的协议。
https://ssl.google-analytics.com:
这允许从 https://ssl.google-analytics.com 加载执行脚本,通常用于包含 Google Analytics 的跟踪代码。
(4). 绕过防御:
为了符合策略, 我们需要输入被允许域名下的js脚本.
打开 https://pastebin.com
网站, 输入一段需要执行的js代码(不需要<script>
标签), 点击 Create New Paste
按钮生成脚本,
在新网页中找到 download
按钮, 右键复制下载链接, 这个链接就可以引用我们刚才输入的js代码了. 比如:
py
https://pastebin.com/dl/ftjb8rHF
2. 中级防御
(1). 观察控制台警告:
py
Content-Security-Policy:忽略 script-src 中的 "'unsafe-inline'":指定了 nonce-source 或 hash-source
Content-Security-Policy:页面设置阻止读取位于 inline 的一项资源("script-src")。
这说明注入的脚本被csp策略拦截, 并且可能与 nonce-source
或 hash-source
有关系.
再看一下前端的代码:
html
<script>alert(111)</script>
这里看到注入的代码被直接写到了html中, 但是无法执行, 那么后端可能做了CSP策略.
(2). 查看响应头:
py
HTTP/1.1 200 OK
Date: Mon, 06 Nov 2023 09:00:40 GMT
Server: Apache/2.4.48 (Unix) OpenSSL/1.1.1k PHP/7.3.29 mod_perl/2.0.11 Perl/v5.32.1
X-Powered-By: PHP/7.3.29
Expires: Tue, 23 Jun 2009 12:00:00 GMT
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';
X-XSS-Protection: 0
Content-Length: 4168
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html;charset=utf-8
(3). 分析响应头:
这里可以看到后端设置了CSP策略, 且禁用了浏览器的 XSS 过滤功能(详见: 五, 其他安全响应头
):
py
Content-Security-Policy: script-src 'self'
'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';
X-XSS-Protection: 0
'self'
: 表示只允许执行来自于同一来源(协议+域名+端口)的脚本。这是一种自我限制,只加载当前源(也就是页面自己)提供的脚本。
'unsafe-inline'
: 允许执行页面内联的脚本,例如直接写在HTML中的
'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA='
: 这个nonce
(一次性数字)用于允许特定的脚本执行。脚本标签必须包含一个匹配的nonce属性。这个方法可以用来允许某些内联脚本执行,同时仍然保持其他的安全限制。
(4). 绕过防御:
为了符合CSP的策略, 在script标签中添加上nonce
属性即可绕过.
js
<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>
五, 其他安全响应头:
1. X-Content-Type-Options
这是一个 HTTP 响应头,用来指示浏览器不应该进行 MIME 类型探测
(MIME sniffing
)。
当这个响应头被设置时,它会告诉浏览器遵守在 Content-Type 头中发送的 MIME 类型。
如果 Content-Type 头中指定的类型是 "text/html",那么浏览器会只以 HTML 的方式解析内容,即使实际的内容可能是 JavaScript。
在没有这个响应头的情况下,一些浏览器会尝试根据实际的内容或资源的开始几个字节来猜测内容的类型。这种行为在某些情况下可能导致安全漏洞。
例如,如果一个攻击者能够上传一个带有可执行代码的文本文件到一个不正确地配置了 MIME 类型的服务器,浏览器可能会将该文本文件当作一个脚本来执行。
通过设置 X-Content-Type-Options: nosniff
,开发者可以较为安全地防止浏览器执行不应当执行的内容,减少一些基于 MIME 类型混淆的攻击,如驻留在服务器上的恶意软件尝试把自身伪装成图片或其他无害文件类型的情况。
这个设置特别对那些用户可以上传内容的网站来说非常重要,因为它有助于防止这些用户上传恶意文件,然后利用浏览器的 MIME 探测逻辑来执行它们。
2. X-XSS-Protection
这是一个 HTTP 响应头,旨在触发特定浏览器内置的跨站脚本(XSS)过滤功能。
这个过滤功能会检查服务器的响应,以寻找潜在的跨站脚本攻击。
py
X-XSS-Protection: 0
这个设置指示浏览器关闭这个保护机制。
通常情况下,这个头部是用来打开 XSS 过滤并设置它的行为的,如下:
X-XSS-Protection: 1
启用 XSS 过滤(通常是浏览器的默认行为)。
如果检测到跨站脚本攻击,浏览器会尝试清理页面,移除不安全的部分。
X-XSS-Protection: 1; mode=block
不仅启用 XSS 过滤,而且如果检测到攻击,浏览器不会展示页面内容,而是会阻止页面加载。
然而,由于浏览器内置的这个机制并不总是有效,它可能会导致误报或未能检测到所有类型的 XSS 攻击.
现代的浏览器和安全实践更推荐使用内容安全策略(Content Security Policy,CSP)来防护 XSS 攻击
。
因此,X-XSS-Protection 头部已经不被推荐使用
,并且在现代浏览器中已经逐渐被淘汰了。
当开发者确定他们的站点不依赖于浏览器的 XSS 过滤功能,并且他们已经有了更强的防护措施(如 CSP)时,他们可能会通过发送 X-XSS-Protection: 0 来禁用这个功能
。
这有助于避免可能的不一致行为,因为 XSS 过滤功能的实现在不同的浏览器之间可能会有所不同。