Safari 11.0.3 无法识别内容安全策略哈希
Content Security Policy hash not recognized by Safari 11.0.3
我有一个元标记,其中包含以下指令:
<meta http-equiv="Content-Security-Policy" content="base-uri 'self'; script-src 'self' 'sha256-s5EeESrvuQPpk2bpz5I3zn/R8Au2DYB1Z+YUH9p0fUE=' 'sha256-PYYfGnkbZ44B9ZBpgv8NbP3MXT560LMfrDSas2BveJo=';">
然后我在页面下方有 2 个内联脚本,每个脚本都应与策略中生成的 shas 之一相匹配。
在 Chrome 和 Firefox 中,我没有收到任何投诉,我的脚本 运行 符合预期。
在 Safari 版本 11.0.3 (13604.5.6) 中,出现以下错误:
Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy
我很困惑为什么!
不幸的是,我无法生成包含其中问题的最小可重现回购 - 较小的示例在 Safari 中对我有用,所以它让我相信它与我的应用程序中的特定内容有关,可能与我在下面尝试的第二件事。
如有任何帮助,我们将不胜感激!
我尝试过的事情:
是否支持哈希?
根据 and the Safari release notes,Safari 10
实现了支持哈希的CSP 2.0
正确的字符集?
以前,我遇到问题是因为我是根据 UTF-8 字符集计算哈希值,但在没有适当的字符集元标记的情况下将 JS 输出到浏览器。当浏览器尝试计算它们时,我的 JS 中的特殊字符被破坏并导致 sha 出现差异。
我不认为这对我有影响,因为 Chrome 和 Firefox 没有发现任何问题,但也许我错了?
unsafe-inline
用于 Safari,然后在 Chrome 和 Firefox 中允许哈希覆盖它?
根据 CSP 规范,unsafe-inline
为 ignored if a hash or nonce is present。 Safari 11也遵循这一点,所以加unsafe-inline
关键字没有效果
原来这是一个字符集问题。
我设法解决了一个最小的可重现问题(经过反复试验,并且非常幸运!)并发现我的一个角色在 Safari 中渲染前后具有不同的 sha。
在 Safari 中渲染之前,字符如下:
Safari渲染完角色后,是这样的(连源码也一样):
奇怪的是,Chrome 和 Firefox 都没有这个问题,所以它要么是 Safari 在渲染后对字符进行规范化,要么是计算 sha256
哈希值的时间不同在浏览器之间。
解决办法是在UglifyJS中关闭字符压缩,让字符保持\uF900
而不是压缩成上图中的单个字符。
我通过 webpack.config.js 文件中的以下选项实现了这一点:
new UglifyJsPlugin({
uglifyOptions: {
output: {
// necessary to stop the minification of escaped unicode sequences into their actual chars.
// some unicode breaks CSP checks in safari
ascii_only: true,
},
},
}),
我已将此事报告给 Apple,看他们是否会考虑解决此问题。
另一个解决方案是确保脚本中的字符串规范化。这可以在 JavaScript 中使用 String.normalize.
实现
规范化确保每个 Unicode 字符都以其规范 (NFC) 形式表示,这似乎使 Safari 中的 CSP 哈希比较有效。因此,如果您要处理任何可能使用重音、非拉丁脚本等的文本,最好对字符串进行规范化。
我有一个元标记,其中包含以下指令:
<meta http-equiv="Content-Security-Policy" content="base-uri 'self'; script-src 'self' 'sha256-s5EeESrvuQPpk2bpz5I3zn/R8Au2DYB1Z+YUH9p0fUE=' 'sha256-PYYfGnkbZ44B9ZBpgv8NbP3MXT560LMfrDSas2BveJo=';">
然后我在页面下方有 2 个内联脚本,每个脚本都应与策略中生成的 shas 之一相匹配。
在 Chrome 和 Firefox 中,我没有收到任何投诉,我的脚本 运行 符合预期。
在 Safari 版本 11.0.3 (13604.5.6) 中,出现以下错误:
Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy
我很困惑为什么!
不幸的是,我无法生成包含其中问题的最小可重现回购 - 较小的示例在 Safari 中对我有用,所以它让我相信它与我的应用程序中的特定内容有关,可能与我在下面尝试的第二件事。
如有任何帮助,我们将不胜感激!
我尝试过的事情:
是否支持哈希?
根据
正确的字符集?
以前,我遇到问题是因为我是根据 UTF-8 字符集计算哈希值,但在没有适当的字符集元标记的情况下将 JS 输出到浏览器。当浏览器尝试计算它们时,我的 JS 中的特殊字符被破坏并导致 sha 出现差异。
我不认为这对我有影响,因为 Chrome 和 Firefox 没有发现任何问题,但也许我错了?
unsafe-inline
用于 Safari,然后在 Chrome 和 Firefox 中允许哈希覆盖它?
根据 CSP 规范,unsafe-inline
为 ignored if a hash or nonce is present。 Safari 11也遵循这一点,所以加unsafe-inline
关键字没有效果
原来这是一个字符集问题。
我设法解决了一个最小的可重现问题(经过反复试验,并且非常幸运!)并发现我的一个角色在 Safari 中渲染前后具有不同的 sha。
在 Safari 中渲染之前,字符如下:
Safari渲染完角色后,是这样的(连源码也一样):
奇怪的是,Chrome 和 Firefox 都没有这个问题,所以它要么是 Safari 在渲染后对字符进行规范化,要么是计算 sha256
哈希值的时间不同在浏览器之间。
解决办法是在UglifyJS中关闭字符压缩,让字符保持\uF900
而不是压缩成上图中的单个字符。
我通过 webpack.config.js 文件中的以下选项实现了这一点:
new UglifyJsPlugin({
uglifyOptions: {
output: {
// necessary to stop the minification of escaped unicode sequences into their actual chars.
// some unicode breaks CSP checks in safari
ascii_only: true,
},
},
}),
我已将此事报告给 Apple,看他们是否会考虑解决此问题。
另一个解决方案是确保脚本中的字符串规范化。这可以在 JavaScript 中使用 String.normalize.
实现规范化确保每个 Unicode 字符都以其规范 (NFC) 形式表示,这似乎使 Safari 中的 CSP 哈希比较有效。因此,如果您要处理任何可能使用重音、非拉丁脚本等的文本,最好对字符串进行规范化。