是否可以在 ES6 模块导入中使用子资源完整性?



给定如下代码:

import { el, mount } from 'https://unpkg.com/redom@3.2.1/dist/redom.es.js';

有没有办法启用子资源完整性验证以确保 CDN 资产返回预期的内容?

在 HTML 文档中,您可以使用<link rel="modulepreload">元素来执行完整性检查,遗憾的是,目前仅在 Blink 浏览器中支持该检查。

// default script
import( "https://unpkg.com/redom@3.2.1/dist/redom.es.js" )
.then( module => console.log( 'from default script:', typeof module.List ) )
.catch( console.error );
<link rel="modulepreload" 
href="https://unpkg.com/redom@3.2.1/dist/redom.es.js"
integrity="sha384-notthecorrectsha">
<script type="module">
import { List } from "https://unpkg.com/redom@3.2.1/dist/redom.es.js";
console.log( 'from module script:', typeof List );
</script>

没有完整性检查的相同代码段:

// default script
import( "https://unpkg.com/redom@3.2.1/dist/redom.es.js" )
.then( module => console.log( 'from default script:', typeof module.List ) )
.catch( console.error );
<link rel="modulepreload" 
href="https://unpkg.com/redom@3.2.1/dist/redom.es.js">
<script type="module">
import { List } from "https://unpkg.com/redom@3.2.1/dist/redom.es.js";
console.log( 'from module script:', typeof List );
</script>

请注意,此检查也适用于"子模块",但不适用于工作线程。

您还必须通过以下方式定义模块

<script type="module" integrity="..." src="https://unpkg.com/redom@3.2.1/dist/redom.es.js">

你特别要求对 ECMAScript 本身进行更改,目前甚至没有提案,所以我预计它不会很快出现。

但是,在UNPKG

的情况下,如果您相信UNPKG和Cloudflare不会弄乱内容,那很好。只要指定版本,npm 和包作者都无法修改文件。

由于 Deno 支持其依赖项的此类导入(并且没有开箱即用的包管理器(,并且 Node 为将来导入非文件 URL 提供了机会,因此这个问题变得更加重要。

虽然@fregante提到的尚未提出提案的内容仍然是准确的,但 https://github.com/WICG/import-maps/issues/174 包括讨论,包括通过引用的幻灯片演示,修改语法时提出的一些问题(例如,传递依赖缓存失效(以支持SRI以及其他可能的替代方案。

您可以使用 RequireJS,并将您的代码转换为 AMD 或 UMD 来实现此目的。 RequireJS有一个钩子onNodeCreated,它允许您在将脚本标记添加到文档之前访问脚本标记。 您可以将sri属性添加到script标记中:

onNodeCreated: function(node, config, module, path( { node.setAttribute('integrity', integrityForModule(; node.setAttribute('crossorigin', 'anonymous'(; }

信用:https://stackoverflow.com/a/37065379

我使用 Webpack(目标是 UMD(和 RequireJS。 将相关模块放在 webpack 配置文件的external部分中,因此模块不会编译到转译的代码中。

我正在研究一个实用的解决方案,这就是我目前所拥有的。我使用来自任何给定特定 URL(unpkg、cdn、github 等(的外部依赖项,并使用 Deno 缓存来自这些 url 的内容,生成带有哈希的锁定文件,然后从中确保缓存和锁定文件用于生成捆绑包,无效时无法生成捆绑包。按照指示从中查看源代码,并通过测试进行处理,以确保捆绑包通常是预期的。稍后根据需要处理外部捆绑包以适应任何给定的用例(缩小等(。

在我的项目中:

DENO_DIR=./cache deno cache --lock=lock.json --lock-write ./deps.js
deno bundle --lock=lock.json ./deps.js ./bundle.js

deps.js 与任何 ES 模块一样,只需一个有效的 ES 模块导入/导出语句列表,可以直接使用(例如在开发中(而不是捆绑包;Deno 还支持导入映射,这将允许本地路径、裸导入和重新映射各种模式(请参阅文档(;我希望实际上既手动审查版本控制的内容,也可能对捆绑包进行其他测试,以确保其外观/工作正常。这基本上可以确保,如果它正常工作,项目拥有的所有内容通常都是受信任的,并且可以重新分发和支持(使用我自己的CSP标头以及从有限/受限环境提供的所有内容(。

此解决方案和答案来自: https://deno.land/manual/linking_to_external_code https://github.com/denoland/deno/issues/6491 显著的: https://github.com/denoland/deno/security/advisories/GHSA-xpwj-7v8q-mcgj

请注意,在实施此答案的解决方案之前,SRI 不会解决源(无论是 URL、CDN、npm 等(,包括在此过程中发生的转换,无论是在项目内部还是之间和交付之间(例如通过 CDN(。显然,审查传入的代码并对其进行适当的处理是确保给定项目随后生成哈希的唯一方法,然后具有一定的完整性期望。

最新更新