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 信息是有局限性的,可能但不限于会有:

  1. 开发环境和生产环境的差异问题
  2. 对于复杂项目本地很难跑遍所有流程,触发所有资源请求

所以通过本地的 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相关推荐

  1. 为什么权重初始化要非对称?为什么权重初始化不能全为0?为什么初始化值不能太大或者太小?介绍下He初始化以及Xavier初始化?

    为什么权重初始化要非对称?为什么权重初始化不能全为0?为什么初始化值不能太大或者太小?介绍下He初始化以及Xavier初始化? 目录

  2. 请你介绍下Logistic回归模型?

    请你介绍下Logistic回归模型? 逻辑回归(Logistic Regression)是机器学习中的一种分类模型(注意,不是回归模型,虽然叫logistic Regression),由于算法的简单和 ...

  3. 简单介绍下我使用了一年多还不知道的Sql server 2005 组件知识

    简介 Microsoft SQL Server 2005 是用于大规模联机事务处理 (OLTP).数据仓库和电子商务应用的数据库平台:也是用于数据集成.分析和报表解决方案的商业智能平台. SQL Se ...

  4. python网站设计理念_简单介绍下python Django框架的历史,设计理念及优势_Django讲解2...

    简单介绍下python Django框架的历史,设计理念及优势 Django是一个高层次的 Python Web 框架,它是一个鼓励快速开发和干净,实用的框架设计.Django可以更容易地快速构建更好 ...

  5. [html] 常见的浏览器内核都有哪些?并介绍下你对内核的理解

    [html] 常见的浏览器内核都有哪些?并介绍下你对内核的理解 Webkit Blink Gecko Trident 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢 ...

  6. 面试问“请介绍下自己”,应聘者就回了两点,立马勾起HR的兴趣!

    最近对HR日常面试的过程挺感兴趣,就去找了一位HR朋友.那天上午他刚好在面试,我就做旁边看,他已经面试了10几个人了,每个应聘者的自我介绍都是千篇一律的,让他感觉很是无趣. 有时候会对照下简历,要是履 ...

  7. 【Day15】介绍下 promise 的特性、优缺点,内部是如何实现的,动手实现 Promise

    介绍下 promise 的特性.优缺点,内部是如何实现的,动手实现 Promise Promise 基本特性 Promise 的优点 Promise 的缺点 简单代码实现 面试够用版 大厂专供版 Pr ...

  8. 面试题:请介绍⼀下 JMM(Java 内存模型)

    面试题:请介绍⼀下 JMM(Java 内存模型) 关键词 CPU缓存一致性协议(例如MESI),多个CPU核心之间缓存不会出现不同步的问题 Store Buffer.Load Buffer和L1之间却 ...

  9. 请使用webdav_介绍下phpdav的使用功能价值

    说明一下:原测试地址域名没有续费已经废弃,新的测试地址: https://webdav.work:8150/ 我用php开发了一个webdav协议的软件phpdav github地址: xinghan ...

最新文章

  1. 使用Forms Authentication 身份验证 之 Basic Knowledge
  2. Android adb无线调试脚本
  3. matlab怎么连接服务器,matlab安装小坑----连接不上服务器
  4. 利用互斥体阻断想哭蠕虫,实现联网升级
  5. 用计算机怎么录资料,如何在短时间内快速录入大量数据 -电脑资料
  6. LeetCode 1419. 数青蛙(脑洞题)
  7. PHP定时抽奖怎么实现的,定时抽奖活动怎么做?
  8. UILongPressGestureRecognizer
  9. python实现qq空间自动点赞
  10. 台式机和计算机有什么区别,笔记本电脑和台式电脑有什么区别
  11. 环球企业家:web2.0创业时代将终结
  12. 关于st-link与stm32f103c8t6连接
  13. 设计模式 ---建造者模式
  14. ps基础知识学习总结
  15. android点赞取消赞功能吗,Android 仿微博的点赞上报功能,持续点赞再取消
  16. 电脑充电器,电脑充电器没带怎么充电
  17. 项目管理-项目开发相关
  18. 递归实现二叉树的前序遍历
  19. layui复选框,设置部分可选
  20. FlexRay总线原理及应用

热门文章

  1. 非常专业的图形图像处理工具-Adobe Illustrator CS5提供下载
  2. 弘辽科技:聚划算收费实施细则介绍
  3. 博弈游戏之三大博弈---bashWythoffNimm
  4. Keras下载数据失败,本地导入
  5. Hive 高级篇(调优)
  6. 移动宽带虚拟网服务器设置,移动宽带路由器怎么设置?
  7. Error - 使用statsmodels报错ModuleNotFoundError: No module named 'pandas.tseries.tools'
  8. 2022年Oracle学习计划
  9. VoLTE的基本介绍
  10. CPU峰值性能计算方法