内容安全策略的主要作用就是尽量降低网站遭受 XSS 跨站脚本攻击的可能。浏览器没办法区分要执行的代码是否为页面本身的还是恶意注入的,XSS 就是利用这一点对网站进行攻击。 ?
CSP 的全称是 Content-Security-Policy 在白名单策略中,可以使用他来指定浏览器仅渲染或执行来自白名单中的资源。即便是被恶意注入了脚本,因为脚本并不在白名单中,因此不会执行。 ?
还可以使用 CSP 指定使用 HTTP 还是 HTTPS 从而避免数据包嗅探攻击
CSP 支持在 html 的 meta 标签中和 HTTP 头中使用
单个指令
语法规则:Content-Security-Policy: <policy-directive>; <policy-directive>
比方说限制 img 标签的 src 只能使用同源的:
Content-Security-Policy: img-src 'self'
在后台创建个简单的服务 ?:
const express = require('express') const app = express() const html = ` <!DOCTYPE html> <head> <meta charset="UTF-8"> </head> <body> <img src="http://httpbin.org/image/png" alt=""> </body> </html> ` app.get('/', function(req, res) { res.set('Content-Security-Policy', "img-src 'self'") res.end(html) res.type('.html') }) app.listen(5500, 'localhost', () => console.log('listening...'))
服务端返回一个 html 数据,其中包含一个 img 标签,src 指向 httpbin.org 这个网站的资源,那么因为同源策略,这个图片不会被显示出来 ❌
可以看到 CSP 策略以及那个生效,页面中的图片没有展示出来
报错如上图 ?
我们尝试修改一下该策略让 httpbin 的资源生效
app.get('/', function(req, res) { + res.set('Content-Security-Policy', img-src http://httpbin.org") res.end(html) res.type('.html') })
这样图片就可以加载出来了
⚠️ 一定要注意在使用策略的时候针对关键字 需要加上引号 不然会被认为是一个服务器
多个指令
针对 XSS 攻击的内联脚本,如果攻击者使用 script 在页面中加载恶意代码会导致严重问题 ❗️
CSP 针对这种攻击也有相应的解决办法——禁止内联脚本,包括 script 标签中的脚本, javascript: 的脚本等
如果非要使用内联脚本,那么一种方式是在 HTTP 头中增加一条 Content-Security-Policy: script-src unsafe-inline 另一种方法是在 Level 2 的 CSP 策略中计算内联脚本的 SHA 哈希值:
<script>alert('Hello, world.');</script> 这个代码的哈希值计算结果放在 CSP 里面: Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
下面是个例子 ?
我们只允许 self 或 75CDN 的 js 资源在页面中能够正常加载:
const html = ` <!DOCTYPE html> <head> <meta charset="UTF-8"> </head> <body> + <script src="https://lib.baomitu.com/jquery/3.3.1/jquery.slim.min.js"></script> + <img onClick="javascript:console.log('hack')" src="http://httpbin.org/image/png" alt=""> + <script src="./main.js"></script> + <script> + alert('hello') + </script> </body> </html> ` app.get('/', function(req, res) { + res.set('Content-Security-Policy', "script-src https://lib.baomitu.com 'self'; img-src http://httpbin.org") res.end(html) res.type('.html') }) + app.use(express.static('public'))
这里我们有两种执行 js 的模式一种是 javascript: 一种是 script 内联标签的形式,在 CSP 中我们设置了只允许 https://cdn.baomitu.com/ 和 self 的 JS 资源
⚠️ 注意书写多个策略应当符合规范:
效果如下:
不出意外,两者都可被正常加载,但内联脚本均无法加载:
当点击 img 标签时报错
其他众多指令还有:
- child-src:为 web workers 和其他内嵌浏览器内容定义 合法的源,例如用 frame 和 iframe 加载到页面的内容。如果开发者希望管控内嵌浏览器内容和 workers,那么应分别使用 frame-src 和 worker-src 指令,而不是child-src。
- connect-src:限制能通过脚本接口加载的 URL。
- default-src:为其他取指令提供备用服务fetch directives.
- font-src:限制通过@font-face加载的字体源。
- frame-src: 限制通过类似 frame 和 iframe 标签加载的内嵌内容源。
- img-src: 限制图片和图标源
- manifest-src : 限制 application manifest 文件源。
- media-src:限制通过 audio 或 video 标签加载的媒体文件源。
- object-src:限制通过 object, embed,applet 标签加载源。
- script-src:限制 javascript 源。
- style-src:限制层叠样式表文件源。
- worker-src:限制 Worker, SharedWorker, 或者 ServiceWorker 脚本源。
详情见 CSP2 文档:https://www.w3.org/TR/CSP2/#directives
事件处理函数
当违反了内容安全策略,浏览器会触发一个名为 securitypolicyviolation 的事件,该事件详细描述了被禁止的 URI 地址、违反的策略指令、时间戳等信息 ?
该事件是在 CSP Level 2 中定义的
document.addEventListener("securitypolicyviolation", (e) => { console.dir(e) })
另外,在 CSP Level 3 中还可以通过构造函数自定义事件:
报告模式和违例报告
另外,CSP 策略可以设置为 report-only,这样 CSP 就不是强制性的,通过指定 report-uri 如果企图违反所建立的策略,那么就会自动发送违规的报告到这个地址上 ?
我们重置代码并增加解析 body 的依赖,在触发违反策略的情况下,服务端打印报告信息 ?
const express = require('express') + const bodyParser = require('body-parser') const app = express() const html = ` <!DOCTYPE html> <head> <meta charset="UTF-8"> </head> <body> + <img src="http://httpbin.org/image/png" alt=""> </body> </html> ` + app.use(bodyParser.json({type: 'application/csp-report'})) app.get('/', function(req, res) { + res.set('Content-Security-Policy', "img-src 'self'; report-uri http://localhost:5500/reporter") res.type('.html') res.end(html) }) + app.post('/reporter', function(req, res) { + res.end() + console.log(req.body) + }) app.use(express.static('public')) app.listen(5500, 'localhost', () => console.log('listening...'))
刷新浏览器打开调试面板:
报告已发送到 report-uri,后台打开终端可看到报告详细信息:
在其他地方使用
html 的 meta 标签也可以配置 CSP
写法如上,将 http-equiv 属性设置为 Content-Security-Policy 指令则写在 content 属性中即可
在代理服务器 nginx 中使用
在 nginx 中使用 add_header 增加 http 头,下面是个例子:
add_header Content-Security-Policy "default-src 'self';"
本文来自:变化吧门户-www.bianhb.com
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧门户观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。
- 赞助本站
- 微信扫一扫
- 加入Q群
- QQ扫一扫
评论