CSP:给大家介绍下我爸爸 — Performance
CSP:给大家介绍下我爸爸 — Performance
这篇文章主要讲如何利用 PerformanceAPI 辅助书写 CSP 指令 里的域名名单的思路,并不是写 CSP 的最佳实践,也未提供成熟工具。如果你还不了解 CSP 或者 PerformanceAPI,可以看下第 0 步(由于我并未特别深入 CSP 规则,如果有写的不对的地方,请大力指正),大佬请跳过。
0. 名词解释
- CSP (Content Security Policy)
可以理解为资源加载 (CSS, img, JS, connect, iframe, media...) 的域名白名单,防止应用加载到有危险的脚本之类的,可以通过 meta 标签书写 or 直接在 Response Header 里写入
- PerformanceAPI (下面简称 Perf )
记录当前页面中与性能相关的信息,打开浏览器 Devtool 里 Network 面板随意查看一个资源的请求的 Timing 其实就可以在 Perf 中找到相关联的数据,例如某字体的下载时间:
Perf 广泛用在前端监控中,这里不多讲。今天只说其中的 PerformanceResourceTiming ,一个记录了特定资源的下载信息的对象。
1. CSP 与 Perf : 需求与供应
先来看一段 Github 的 CSP 内容:
Content-Security-Policy:- default-src 'none';- base-uri 'self'; - block-all-mixed-content; - connect-src 'self' uploads.github.com status.github.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; - font-src assets-cdn.github.com;- form-action 'self' github.com gist.github.com;- frame-ancestors 'none'; - frame-src render.githubusercontent.com; - img-src 'self' data: assets-cdn.github.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; - manifest-src 'self'; - media-src 'none';- script-src assets-cdn.github.com; - style-src 'unsafe-inline' assets-cdn.github.com
可以看到是非常详细的,尤其是 connect-src 那长长长长长长长长的一串。那么问题来了,如果让你你在项目中去编写这段规则,你会怎么做呢:
首先想到的肯定是手动穷举,但在中小项目且你清晰全链路的情况下这样做没什么问题,甚至 Policy 全部降级到 default-src 问题也不大。但穷举缺点也很明显:
1. 项目一大或者是接手的项目就很浪费时间
2. Policy 拆分可能得看你的心情
3. 懒怎么办 。
说白了除非有具体硬指标,否则 CSP 也只是一个锦上添花的东西,大部分开发日常可能都不会重视,不重视怎么可能会花心思去做好呢,那么,有没有这么一款工具或者一段代码能够直接 根据项目 生成足够可用的 CSP 指令呢!!!
并没有。
不过不过不过轮到主角登场了,我们可以借助 Perf 来 得知项目会获取哪些资源,试着在控制台运行:
function assetParser(url) {const { host, pathname } = new URL(url)const assetName = pathname.split('/').pop()return { assetType: assetName.includes('.') ? assetName.split('.').pop() : null, name: assetName || host, host}
}performance.getEntriesByType('resource').map(({ name, initiatorType }) => {return {initiatorType,...assetParser(name)}})
你会看到(下图为Chrome Canary 68 版本的输出,为什么要强调浏览器版本呢,大概你也猜到了,后面会有说明):
我们单独拎出一个来粗略地说明下
- assetType: 资源类型
这个字段并非 Perf 提供,而是我们根据请求的 url 切割出来的,如果该字段:
- 有具体值 —— 那么在大部分情况下足以说明该资源的类型,常见为一些图片格式(png / jpg / svg...)、JS、CSS【诶!是不是感觉可以和 CSP 里的 img-src / script-src / style-src / font-src ... 对应上了呢?】
- 为 null —— 这种情况下说明路径没有 extension 或者不是个普通的静态资源,这时需要我们再来看的另一个字段:initiatorType
- initiatorType: 发起者类型
这是 Perf 内置的属性,表明了该资源的发起者(某个 API or 某 DOM.localName)是什么类型。
例如我们看到值是 link,那么说明该资源是通过 link 标签加载的(直接加载或者 preload / prefetch)其余的值例如:
- 如果是 iframe 、video、audio、img 则说明是通过对应标签加载的【咦!是不是也对应 CSP 内 frame-src 和 media-src、img-src了】
- 如果是 fetch、xhrhttprequest、beacon 则说明是请求【对!可以对应 connect-src 】
But,但在更多情况下,这个字段是有迷惑性的,因为有时它并不直接代表资源类型。
例如一张图片资源类型是一张图片,为什么 initiatorType 会是 css 呢?因为根据定义,这张图片很可能是 CSS 里的 background-image 通过 url 方法加载的,所以它的发起者的类型是 css ,那么这就会引出一个问题:
如果这种背景图片不是以显示的资源路径( https://host/path/avatar.jpg ) 的形式加载,而是以( https://host/path/avatar ) 这种接口进行加载,那么这是 assetType 的判断肯定是无效,而 initiatorType 又是 css,这时候工具(我们假设有个自动生成 CSP 的工具,这也是之后最终的目的)该把这个 host 写在 CSP 的 img-src 下呢还是 style-src 下呢?
目前的想法是这一类的资源都应该降级为 default-src,或者交给人工处理(这不废话吗
写到这里,CSP 的需求 — Perf 的供应关系貌似已经明朗。配合 assetType / initiatorType / host 三个字段配合,再写几行代码,目前看来足以生成囊括 img-src / script-src / style-src / font-src / frame-src / media-src / connect-src 以及 default-src 的 CSP 指令了。
但是!又来但是了,事情真的这么顺溜儿吗,前面提到截图是来自 Chorme Canary 68 版本,一个很新很新的版本,为什么要用这个版本呢,因为到目前为止 (2018-08-02) ,Chrome 对 initiatorType 判断还存在着 Bug ,这是个2016年被提出的 Bug,到现在还未在正式版完整的解决,这里提到有一段:
老版本内核发起的 fetch 、 video 等的 initiatorType 可能为空字符串!!具体涉及哪些版本这里并未深究。因为存在这一个 Bug 对于 connect-src 的控制类似乎走不通了?答案是:
对的,走不通
或许你会说可以用 Canary 在自己本地跑项目然后收集 Pref 信息不就行了?
对的,但这也正是要抛出的一个问题 —— 我们在本地跑项目获取到的 Perf 信息是有局限性的,可能但不限于会有:
- 开发环境和生产环境的差异问题
- 对于复杂项目本地很难跑遍所有流程,触发所有资源请求
所以通过本地的 Perf 方式想监控到 fetch 的资源也不完美
2. 那蹊径有吗蹊径
PerformanceAPI 主要应用于 —— 前端监控服务
那么,如果我们将这些资源数据上报到监控服务,在一定量级后,我们捞出的聚合数据就可以解决上面说的环境差异和覆盖率问题,对于除 fetch 外的其他资源的 CSP 规则生成都是没问题了。为什么这里 fetch 还是被排除了?
因为你没办法保证用户的客户端没有 initarortype 的问题,所以对于 connect-src ,再有监控服务的情况下,最好的办法还是进行 fetch 和 xhr 拦截上报,然后捞出数据获取 host。
写着写着感觉文章有种烂尾的感觉了,因为好像没有提供一个清晰的结局方案,那么我们试着来总结下:
3. 最终看起来可行的方案
- 静态资源通过 performance.getEntriesByType('resource') 获取信息进行上报,然后可以对应 img-src / script-src / style-src / font-src / frame-src / media-src ... 基本上除了 connect-src之外的绝大部分(也是够用的) CSP 字段
- 动态资源(接口请求等) 通过请求拦截器进行上报,用来拼接 connect-src
- 对于那些无法判断类型的资源则用 default-src 进行降级
最终当然要提供一个可视化工具,允许用户选择需要的指令:
4. 参考链接
- Content-Security-Policy
- PeformanceResourceTiming
- initiatorType
- Chrome initiatorType Bug Report
- HTML elements that could trigger an HTTP request
CSP:给大家介绍下我爸爸 — Performance相关推荐
- 为什么权重初始化要非对称?为什么权重初始化不能全为0?为什么初始化值不能太大或者太小?介绍下He初始化以及Xavier初始化?
为什么权重初始化要非对称?为什么权重初始化不能全为0?为什么初始化值不能太大或者太小?介绍下He初始化以及Xavier初始化? 目录
- 请你介绍下Logistic回归模型?
请你介绍下Logistic回归模型? 逻辑回归(Logistic Regression)是机器学习中的一种分类模型(注意,不是回归模型,虽然叫logistic Regression),由于算法的简单和 ...
- 简单介绍下我使用了一年多还不知道的Sql server 2005 组件知识
简介 Microsoft SQL Server 2005 是用于大规模联机事务处理 (OLTP).数据仓库和电子商务应用的数据库平台:也是用于数据集成.分析和报表解决方案的商业智能平台. SQL Se ...
- python网站设计理念_简单介绍下python Django框架的历史,设计理念及优势_Django讲解2...
简单介绍下python Django框架的历史,设计理念及优势 Django是一个高层次的 Python Web 框架,它是一个鼓励快速开发和干净,实用的框架设计.Django可以更容易地快速构建更好 ...
- [html] 常见的浏览器内核都有哪些?并介绍下你对内核的理解
[html] 常见的浏览器内核都有哪些?并介绍下你对内核的理解 Webkit Blink Gecko Trident 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢 ...
- 面试问“请介绍下自己”,应聘者就回了两点,立马勾起HR的兴趣!
最近对HR日常面试的过程挺感兴趣,就去找了一位HR朋友.那天上午他刚好在面试,我就做旁边看,他已经面试了10几个人了,每个应聘者的自我介绍都是千篇一律的,让他感觉很是无趣. 有时候会对照下简历,要是履 ...
- 【Day15】介绍下 promise 的特性、优缺点,内部是如何实现的,动手实现 Promise
介绍下 promise 的特性.优缺点,内部是如何实现的,动手实现 Promise Promise 基本特性 Promise 的优点 Promise 的缺点 简单代码实现 面试够用版 大厂专供版 Pr ...
- 面试题:请介绍⼀下 JMM(Java 内存模型)
面试题:请介绍⼀下 JMM(Java 内存模型) 关键词 CPU缓存一致性协议(例如MESI),多个CPU核心之间缓存不会出现不同步的问题 Store Buffer.Load Buffer和L1之间却 ...
- 请使用webdav_介绍下phpdav的使用功能价值
说明一下:原测试地址域名没有续费已经废弃,新的测试地址: https://webdav.work:8150/ 我用php开发了一个webdav协议的软件phpdav github地址: xinghan ...
最新文章
- 使用Forms Authentication 身份验证 之 Basic Knowledge
- Android adb无线调试脚本
- matlab怎么连接服务器,matlab安装小坑----连接不上服务器
- 利用互斥体阻断想哭蠕虫,实现联网升级
- 用计算机怎么录资料,如何在短时间内快速录入大量数据 -电脑资料
- LeetCode 1419. 数青蛙(脑洞题)
- PHP定时抽奖怎么实现的,定时抽奖活动怎么做?
- UILongPressGestureRecognizer
- python实现qq空间自动点赞
- 台式机和计算机有什么区别,笔记本电脑和台式电脑有什么区别
- 环球企业家:web2.0创业时代将终结
- 关于st-link与stm32f103c8t6连接
- 设计模式 ---建造者模式
- ps基础知识学习总结
- android点赞取消赞功能吗,Android 仿微博的点赞上报功能,持续点赞再取消
- 电脑充电器,电脑充电器没带怎么充电
- 项目管理-项目开发相关
- 递归实现二叉树的前序遍历
- layui复选框,设置部分可选
- FlexRay总线原理及应用