Content Security Policy

tech2024-07-23  47

Content Security Policy(内容安全策略,简称csp)用于检测并阻止网页加载非法资源的安全策略,可以减轻xss攻击带来的危害和数据注入等攻击。本文讲述的内容主要有如何使用csp和业务接入csp流程这两部分。

简介

csp主要工作是定义一套页面资源加载白名单规则,浏览器使用csp规则去匹配所有资源,禁止加载不符合规则的资源,同时将非法资源请求进行上报。

csp作用:减轻网页被xss攻击后所受到的危害。实际上csp是在xss攻击发生后才起作用,阻止请求注入的非法资源,csp并不是直接阻止xss攻击的发生。

使用方式

csp主要有两种使用方式,分别是设置响应头Content-Security-Policy和使用meta标签。

响应头

在网页html请求的响应头中进行定义,定义方式:

Content-Security-Policy: 指令1 指令值1 指令值2; 指令2 指令值1;

例子:

Content-Security-Policy: srcipt-src 'self' *.test.com'; img-src: https: data:;

后面会重点讲解csp中具体存在哪些指令和指令值,可以在定义规则中看到具体的介绍。

meta

在网页html文件中进行定义,定义方式:

<meta http-equiv="Content-Security-Policy" content="指令1 指令值1 指令值2; 指令2 指令值1;" >

例子:

<html> <head> <meta http-equiv="Content-Security-Policy" content="srcipt-src 'self' *.test.com'; img-src: https: data:;" > </head> <body>...</body> </html>

注意:由于html文档是从上至下进行解析,因此,meta尽量写在最前面,保证能够对所有资源请求进行约束。

效果

csp规则匹配到的资源都能够正常请求,一旦有非法资源请求,浏览器就会立即阻止,阻止的效果如下

定义规则

csp规则内容主要由指令和指令值这两部分构成,指令用于定义资源类型,指令值用于定义资源请求地址规则。

例子:

Content-Security-Policy: srcipt-src 'self' *.test.com';

上面csp规则中,script-src是脚本资源加载指令,'self'和*.test.com是指令值,当加载js脚本的时候,只有满足指令值定义的规则才能正常加载。

指令

指令说明示例default-src定义所有类型资源默认加载策略,当下面这些指令未被定义的时候,浏览器会使用default-src定义的规则进行校验'self' *.test.comscript-src定义JavaScript资源加载策略'self' js.test.comstyle-src定义样式文件的加载策略'self' css.test.comimg-src定义图片文件的加载策略'self' img.test.comfont-src定义字体文件的加载策略'self' font.test.comconnect-src定义XHR、WebSocket等请求的加载策略,当请求不符合定义的规则时,浏览器会模拟一个响应状态码为400的响应'self'object-src定义<object> <embed> <applet>等标签的加载策略'self'media-src定义<audio> <video>等多媒体html标签资源加载策略'self'frame-src【已废弃】定义iframe标签资源加载策略(新指令:child-src)'self'sandbox对请求的资源启用sandbox,类似于iframe中的sandbox功能allow-scriptsreport-uri定义上报地址。当有资源被拦截的时候,浏览器带着被拦截的资源信息请求这个uri。(只在响应头中定义才能生效)https://csp.test.com/reportchild-src【csp2】定义子窗口(iframe、弹窗等)的加载策略'self' *.test.comform-action【csp2】定义表单提交的源规则'self'frame-ancestors【csp2】定义当前页面可以被哪些页面使用ifram、object进行加载'self'plugin-types【csp2】定义页面允许加载哪些插件

指令值

指令值说明示例*所有内容都正常加载img-src *;'none'不允许加载任何资源img-src 'none';'self'允许加载同源资源img-src 'self';'unsafe-inline'允许加载inline内容(例如:style、onclick、inline js、inline css等)srcript-url 'unsafe-inline';'unsafe-eval'允许加载动态js代码(例如:eval())script-src 'unsafe-eval';http:允许加载http协议资源img-src http:;https:允许加载https协议资源img-src https:;data:允许使用data协议(css中加载base64图片使用的就是data协议)img-src data:;域名允许加载该域名下所有https协议资源img-src test.com;路径允许加载该路径下所有https协议资源img-src test.com/img/;通配符*.test.com允许加载子域名下所有https协议的资源(任意子域名);*://*.test.com:*这个匹配逻辑原意是任意协议、任意子域名、任意端口,但在实际测试过程中发现这个指令值没有任何作用img-src *.test.com;

实际业务开发

前面我们了解了csp基本用法,接下来讲述下业务接入csp流程。由于浏览器会禁止加载违反csp规则的资源,因此,我们需要对csp进行一系列验证后,才能正式上线,下面将带着大家一步一步的完成业务csp规则的部署。

整理资源地址

web页面中,浏览器有提供performance.getEntries()接口,用于获取网页加载的资源信息,其中initiatorType(资源类型)属性可以知道资源属于哪个指令,name(资源地址)可以定义指令值有哪些。

为了简便,下面将只定义default-src这一个指令,所有资源加载检测都直接走default-src定义的规则。

const entries = window.performance.getEntries() const names = entries.map(info => info.name);

生成csp规则

const parseReg = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/; const httpList = []; const httpsList = []; const otherProtocol = []; // 分离https、http、其他协议资源 names.forEach(str => { const parse = parseReg.exec(str); // 实测 *://*.test.com:*并不能匹配test.com任意协议、任意子域名、任意端口,因此这里将http和https分离 if (['https', 'http'].includes(parse[1])) { const domain = parse[3]; const midProduct = domain.split('.'); const midProductLen = midProduct.length; const childDomain = midProductLen > 2 ? `*.${midProduct.slice(midProductLen - 2).join('.')}` : domain; if (parse[1] === 'https') { httpsList.push(childDomain); } else { httpList.push(`http://${childDomain}`); } } else { otherProtocol.push(parse[1]); } }); const domains = new Set(otherProtocol.concat(httpsList).concat(httpList)) const defaultSrc = [...domains].join(' ');

这样我们就能够得到一个比较简单的default-src指令值,如果项目中还存在inlin的代码或者使用了eval函数动态执行js代码,就需要在default-src中配置额外的值,具体应该添加什么内容,大家可以在指令值看到。

本地测试

在谷歌浏览器插件应用,添加CSP Mitigator插件,然后配置页面地址、csp规则信息。

插件内容配置完成以后,点击start开始测试。进入业务页面,查看控制台有哪些资源不符合csp规则,然后再根据报错将csp规则补全。

如果本地测试,没有出现违背csp规则的资源加载时,就可以考虑将csp规则放到现网进行测试。只是此时放入现网使用的响应头不是Content-Security-Policy,而是Content-Security-Policy-Report-Only,下面一个板块将详细介绍现网测试。

现网测试

一般情况下,浏览器会禁止加载违反csp规则的资源,这对于csp准确度要求比较高,如果我们不小心遗漏了某个规则,将会影响到页面正常展示,这无疑会给开发者带来巨大的压力。浏览器为了解决这一问题,提供了Content-Security-Policy-Report-Only响应头,对于违反csp规则的资源,只进行上报处理,不禁止加载资源,这样我们可以在不影响业务使用的情况下,使用上报的非法资源数据,来逐渐补全csp规则。

资源被阻止后,浏览器上报的内容如下:

如果上报的被阻止数据中存在合法资源,我们就需要将该资源写入规则中,更新后,我们可以将规则写到CSP Mitigator插件中,然后在页面控制台中使用fetch函数去请求之前上报的资源地址,如果控制台没有报禁止加载的问题,那说明最新的csp规则是有效的。经过一段时间优化后,csp优化好以后,继续写入Content-Security-Policy-Report-Only响应头中进行观察。

正式上线

如果确认csp上报的资源只有非法资源了,此时便可以将响应头改成Content-Security-Policy。当响应头改为Content-Security-Policy以后,违背csp规则的资源会被禁止加载,同时会进行上报处理。

参考

Content Security Policy 介绍Content Security Policy 入门教程Content Security Policy Level 2 介绍Content Security Policy (CSP)performance.getEntries()
最新回复(0)