介绍:
HTTP 响应头 Content-Security-Policy
允许站点管理者控制用户代理能够为指定的页面加载哪些资源。主要针对js代码的执行,针对xss进行防御,CSP设置参数较多,这里针对常用的参数和内容进行讲解,加深对csp的理解和运用。
常用指令:
指令名 | demo | 说明 |
---|---|---|
default-src | ‘self’ cdn.example.com | 默认策略,如果某个 fetch 指令在 CSP 头部中未定义,那么浏览器就会寻找default-src 指令的值来替代。 当设置了default-src后会影响以下标签,如果未另行设置则统一按照default-src设置走: child-src,connect-src,default-src,font-src,frame-src,img-src,manifest-src,media-src,object-src,script-src,style-src,worker-src |
script-src | ‘self’ js.example.com | 对js代码执行进行过滤设置 |
style-src | ‘self’ css.example.com | 定义css文件的过滤策略 |
img-src | ‘self’ img.example.com | 定义图片文件的过滤策略 |
connect-src | ‘self’ | 定义请求连接文件的过滤策略 |
font-src | font.example.com | 定义字体文件的过滤策略 |
object-src | ‘self’ | 定义页面插件的过滤策略,如 <object>, <embed> 或者<applet> 等元素 |
media-src | media.example.com | 定义媒体的过滤策略,如 HTML6的 <audio>, <video> 等元素 |
frame-src | ‘self’ | 定义加载子frmae的策略 |
指令值:
值 | demo | 说明 |
---|---|---|
* | img-src * | 允许任意地址的url,但是不包括 blob: filesystem: schemes. |
‘none’ | object-src ‘none’ | 所有地址的咨询都不允许加载 |
‘self’ | script-src ‘self’ | 同源策略,即允许同域名同端口下,同协议下的请求 |
data: | img-src ‘self’ data: | 允许通过data来请求咨询 (比如用Base64 编码过的图片). |
domain.example.com | img-src domain.example.com | 允许特性的域名请求资源 |
*.example.com | img-src *.example.com | 允许从 example.com下的任意子域名加载资源 |
https://cdn.com | img-src https://cdn.com | 仅仅允许通过https协议来从指定域名下加载资源 |
https: | img-src https: | 只允许通过https协议加载资源 |
‘unsafe-inline’ | script-src ‘unsafe-inline’ | 允许行内代码执行 |
‘unsafe-eval’ | script-src ‘unsafe-eval’ | 允许不安全的动态代码执行,比如 JavaScript的 eval()方法 |
实际使用:
default-src:
再实际使用中如果使用了 default-src,类似如下代码:
<%
response.setHeader("Content-Security-Policy","default-src 'self'");
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
hello world!
<script>
alert(1);
console.log(eval('2 + 2'));
</script>
</body>
</html>
执行后会报错,是因为我们设置了default-src,相当于也设置了script-src为self
所以当我们再设置default-src的时候一定要注意会不会对页面功能造成影响,如果页面功能复杂建议不要设置这个,采用单独设置,当然页面功能简单可以使用。
connect-src:
测试代码如下使用connect-src会影响远程连接:
<a ping="https://not-example.com">
<script>
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://not-example.com/");
xhr.send();
const ws = new WebSocket("wss://not-example.com/");
const ws2 = new EventSource("http://localhost:1234/");
const es = new EventSource("https://not-example.com/");
navigator.sendBeacon("https://not-example.com/", {
});
</script></a
>
当我们设置为如下:
response.setHeader("Content-Security-Policy","connect-src *"); response.setHeader("Content-Security-Policy","connect-src not-example.com");
测试发现使用*可以对任意协议进行访问,但是只能使用对应的网址仅允许http协议。
response.setHeader("Content-Security-Policy","connect-src 'self'");
当使用self,会默认当前url,注意其子域名也不被允许
script-src:
针对script-src这个设置的算是比较多的,也是我们防御中比较重要的,设置这个要配合对页面进行设置:
response.setHeader("Content-Security-Policy","script-src 'self'");
当被设置为了以上模式:下列两种代码均不能执行:
<script src="https//aaa.localhost:1234/js/library.js"></script> <button id="btn" οnclick="doSomething()">
按钮的需要修改为以下代码才可以执行,上述代码会报错。
document.getElementById("btn").addEventListener('click', doSomething);
<%
response.setHeader("Content-Security-Policy","script-src 'self'");
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
hello world!
<script src="http://www.localhost:1234/test.js"></script>
</body>
</html>
js代码:
alert(1);
console.log(eval('2 + 2'));
执行时可以发现
可以看到虽然设置了self但是对应js代码中的eval是不能执行:
如果是页面中内嵌的script代码,使用self是无法执行,想要执行有两种方法
response.setHeader("Content-Security-Policy","script-src 'nonce-2726c7f26c'"); response.setHeader("Content-Security-Policy","script-src 'sha256-ThXj6yK/lTbpsAsMwOpQuhWzHJkOL99FVjTSwNXadDA='");
页面js代码如下:
<script nonce="2726c7f26c">
console.log(eval('2 + 2'));
</script>
<script>console.log(eval('4 + 4'));</script>
如果采用sha256则要注意加密的内容,不包含<script>另外要注意空格等特殊符号
当然如果不想这么麻烦,想直接可以运行页面内嵌的脚本可以使用'unsafe-inline'
response.setHeader("Content-Security-Policy","script-src ''unsafe-inline'");
如果要执行eval内容需要添加'unsafe-eval'
response.setHeader("Content-Security-Policy","script-src 'nonce-2726c7f26c' 'sha256-ThXj6yK/lTbpsAsMwOpQuhWzHJkOL99FVjTSwNXadDA=' 'unsafe-eval' ");
如果使用‘none’将会关闭一切js代码执行,即禁止页面执行任何js代码,外部内联均禁止
总结:
首先是对执行来源的控制,使用'self'或自己设置可以保证访问或来源的数据是安全的。
然后就是对js代码执行的控制,使用中最安全的为使用sha256对代码进行计算后,虽然这样可以保证仅会执行固定的代码,但是不适用于比较复杂逻辑的系统,使用nonce-***,这种可以保证我们允许执行的js空间是可控的,如果其他标签未过滤导致了xss也无法执行,但是要保证每次返回的数字为一个随机数据,不能被预测,否则攻击者可以拼接预测的结果执行js,导致xss,最后就是使用'unsafe-inline',使用参数则相当于关闭了对页面js执行的控制,这样使用csp意义就不大了,最后就是视情况使用'unsafe-eval'。
插件:
在工作中可以使用插件CSP Evaluator,在浏览器应用商店下载,下载完成后,访问页面的时候可以显示其csp设置是否安全: