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.com |
script-src | 定义 JavaScript 资源加载策略 | 'self' js.test.com |
style-src | 定义款式文件的加载策略 | 'self' css.test.com |
img-src | 定义图片文件的加载策略 | 'self' img.test.com |
font-src | 定义字体文件的加载策略 | 'self' font.test.com |
connect-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-scripts |
report-uri | 定义上报地址。当有资源被拦挡的时候,浏览器带着被拦挡的资源信息申请这个 uri。(只在响应头中定义能力失效) | https://csp.test.com/report |
child-src | 【csp2】定义子窗口(iframe、弹窗等)的加载策略 | 'self' *.test.com |
form-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 规定的部署。
整顿资源地址
<!– window.performance.getEntries().map(info => info.name).filter(str => !!~str.indexOf(‘:’)) –>
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()