CSP(Content-Security-Policy)是一个HTTP response header, 它描述允许页面控制用户代理能够为指定的页面加载哪些资源, 可防止XSS攻击
使用方式:
Content-Security-Policy: 指令1 指令1的值1 指令1的值2 指令1的值3; 指令2 指令2的值1 指令2的值2
调试工具: Chrome插件——modheader。通过随意设置响应头来测试CSP
font-src
frame-src
img-src
script-src
media-src
style-src
...等等,其他参考MDN
这些src规定了页面只能加载里面所设置的font、iframe、img、script...
这些资源,比如有一个html页面的response header是:
Content-Security-Policy: img-src a.b.c; script-src 'unsafe-inline' a.b.c; style-src 'self'
表示只能加载来自a.b.c
域的图片、a.b.c
域的脚本和行内脚本(如<script>console.log(1)</script>
)、只能加载自己域下的style
这些xx-src,一般常见的配置有:
host配置
可精确匹配也可通配符匹配:
https://*.qq.com
https://a.b.com
*.qq.com
www.qq.com
最后,有一个通用化配置——default-src,你给了它什么值,其他几个指令就默认什么值。其他指令如果有设置,那自身的值会覆盖default-src
的值
schema配置
data: => dataURI,比如base64
blob: => blob资源
http: => 顾名思义
https: => 顾名思义
...一般是这些比较多,其他参考MDN
注意,要写冒号。为了防止误解,所以上面用 =>
描述了。eg:
Content-Security-Policy: img-src http: data:; style-src 'self'
self
只能加载自身相同的域资源,其他如data:
, blob:
就不能了
unsafe-inline
行内,一般针对于style和script标签。没有这个,就不能使用行内标签了
如果设置了上报指令的上报地址,当页面有加载不合法的资源,将会往那里上报。比如
Content-Security-Policy: img-src www.qq.com; report-uri https://a.b.c/report
当设置这个header的页面加载www.qq.com
之外的图片的时候,将会阻塞加载,并在控制台报错,再上报到https://a.b.c/report
上
上报的数据就是这些,浏览器帮你拼装好了。当然,随便写的路径肯定是404的,这个自己起一个服务器就可以收到这个上报
目前阶段,一般使用report-uri
上报,用法是后面接上上报地址。report-to
是另一个上报指令,功能更丰富,使用方式稍微麻烦一点
Content-Security-Policy: report-uri https://a.b.c/report
Content-Security-Policy: report-uri /current_page_report
其他的指令比较简单,但使用场景可能不是很多,有兴趣去MDN看看
使用Content-Security-Policy-Report-Only
头而不是直接使用Content-Security-Policy
。Content-Security-Policy-Report-Only
顾名思义,只是上报,不会阻塞资源加载。因此页面改造第一步是先通过仅仅上报的头来观察一段时间,看看哪些资源哪些case是不符合CSP的,漏掉的加上,不合理的干掉
初始化资源指令,给default-src
一个'self'
,让资源都默认走本地。其次通过其他方式改变header(我使用了浏览器插件的方式,比较简单暴力。其实还可以自己开个服务器做代理、本地起nginx加头等等方式都可以),观察控制台报错,再把漏掉的资源补齐,如cdn站点、base64的data:、第三方sdk、图片cos存储地址等都是最常见的case
补齐资源,让页面不再报错;一些是不太优雅或者有风险的case,自己再另外衡量要不要换另一种方式引入或者去掉
另外,如果有iframe、worker、websocket这些的,也需要配置一下CSP
观察一段时间后,自己的上报站点如果有CSP报错,那么去解决掉,然后继续观察一段时间重复同样的步骤,直到没有CSP错误。当上报站点再也没有CSP错误或者错误比较少能接受范围内,将Content-Security-Policy-Report-Only
换成Content-Security-Policy
再次上线
线上不可能也要人家装插件啊、代理啊,如何修改这个头
一般页面就在nginx上对html配response header
location ~* .(html)$ {
add_header Content-Security-Policy "img-src http: data:; style-src 'self'";
}
如果是ssr项目或者前后端不分离项目,服务端直接setheader即可
如果是新需求可能涉及到新的资源引入怎么办
确定知道的源,新增header配置;不确定的最好自己设置一个中转服务,或者重新思考一下需求/技术方案合理性;实在没办法,需要删除default-src设置,并且img-src需要妥协一下了
如果有新页面上线且旧页面已经不使用report-only了怎么办
nginx配置一下,不同的页面使用不同的头
location /a {
add_header Content-Security-Policy "img-src http: data:; style-src 'self'";
}
location /b {
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri https://a.b.c/report";
}
服务端的话,判断一下页面路径,采用不同的setheader方式
页面又安全一分了,心里踏实,起码可以防止很多很多低端攻击手法了。再回头翻翻网上那些经典的攻击手法和案例,是不是可以防掉很多了。不过还是有办法破解的。。。前面说了用什么来调试来着?有想法的,评论区留下自己的想法或者方案,一起交流一下吧