Is it possible to use subresource integrity with ES6 module imports?
Asked Answered
B

5

37

Given code like this:

import { el, mount } from 'https://unpkg.com/[email protected]/dist/redom.es.js';

is there some way to enable subresource integrity verification to ensure that the CDN asset returns the expected content?

Bourque answered 21/8, 2017 at 19:58 Comment(6)
What can I do to improve my question? What's the problem? I don't know what I did wrong, and why I have that downvote.... :/Bourque
I don't have an actual answer, though I suspect the answer is no because this stuff is so new and the spec isn't really done yet. I also edited your question to drop some unneeded details and make the question clearer.Divulgate
Have you tried setting the integrity attribute on a <script type="module"> in a browser that supports both features (i.e. Chrome 61)?Wobbling
That's working, but it doesn't solve my problem in general, because I only can require from each script inside my module. And in my main module, I need to load the dependency.Bourque
@jens1o, it looks like there's some discussion of having this be based on a <link rel="preload"> with an integrity attribute: github.com/w3c/webappsec-subresource-integrity/issues/26 and github.com/w3c/webappsec-subresource-integrity/issues/70Wobbling
It seems like the type of thing where you'd expect a query parameter or something, but I don't know that there's any discussion of that yet.Divulgate
T
9

From an HTML document, you can use the <link rel="modulepreload"> element to perform that integrity check, which is unfortunately currently supported only in Blink browsers.

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

The same snippet without the integrity check:

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

Note that this check would also apply to "sub-modules", but not to Workers.

Tenney answered 22/7, 2020 at 8:14 Comment(2)
Note that this only works in Chrome. I just tested it in Safari 16 and Firefox 105 and the first example loads without errors.Medicament
@Medicament yes, "which is unfortunately currently supported only in Blink browsers.".Tenney
M
5

You have to also define the module via

<script type="module" integrity="..." src="https://unpkg.com/[email protected]/dist/redom.es.js">

What you're asking specifically requires changes to ECMAScript itself and currently there's not even a proposal for it, so I don't expect it to appear anytime soon.


However in the case of UNPKG, if you trust UNPKG and Cloudflare not to mess with the content, you're fine. Neither npm nor the package author can modify the file as long as you specify the version.

Medicament answered 22/1, 2020 at 5:33 Comment(4)
In addition to trusting the servers, you must also trust the network connection to them. In oppressive regimes national ISPs could collude with certificate authorities by law.Etty
Kinda, but if they can change content served via HTTPS, nothing can save you; they already changed the HTML page you loaded, including the dropping any integrity attributes.Medicament
Maybe. I'm working with blockchains where the primary content is far more reliable. Same might be more true for things like electron and cordova apps, or if your primary host simply had a better certificate system than unpkg. Cloudflare protects so much of the internet it would be a far more highly valued door than a small host. More checks always help integrity. But nor do they prove it.Etty
Beware that this won't work in Safari today. As of Safari 14.0.1 (which is the current latest version), if you put in a <script> tag with broken integrity and then later import the same URL, the <script> tag will fail to load, but the import will initiate an additional network request and then run the code.Northwards
R
4

With Deno supporting such imports for its dependencies (and having no out-of-the-box package manager) and Node leaving open the chance for importing non-file URLS in the future, this issue becomes even more important.

While what @fregante mentions about there not being yet a proposal remains accurate, https://github.com/WICG/import-maps/issues/174 includes discussion, including via a referenced slide presentation, of some of the questions raised in modifying the syntax (e.g., transitive dependency cache invalidation) to support SRI as well as other possible alternatives.

Rossierossing answered 18/7, 2020 at 2:42 Comment(0)
S
0

You can use RequireJS, and transpile your code to AMD or UMD to achieve this. RequireJS has a hook onNodeCreated, which gives you access to the script tag before it is added to document. You can add the sri attribute onto the script tag:

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

credit: https://stackoverflow.com/a/37065379

I use Webpack (with a target of UMD) and RequireJS. With the relevant modules put in the external section of the webpack configuration file, so the modules are not compiled into the transpiled code.

Socialization answered 18/3, 2018 at 20:14 Comment(0)
N
0

I'm working through a practical solution and this is what I have so far. I consume external dependencies from any given specific URL (unpkg, cdn, github, etc) and use Deno to cache the content from those urls, generate a lock file with the hashes, then from that ensure the cache and lock file are used to generate a bundle, failing to generate the bundle when invalid. Reviewing the source from this as indicated and process with testing which ensures the bundle is generally what's expected. Later processing the external bundle as desired to fit any given use-case (minify, etc).

in my project:

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

deps.js simply has a list of valid es module import/export statements as with any es module and can be used directly (eg in development) instead of the bundle; Deno also supports import maps which would allow local paths, bare imports and remapping various patterns (see the docs); I expect to actually both manually review version controlled content and possibly have other testing against the bundle to ensure it's looks/works right. This basically ensures, if it works properly, everything the project has is generally trusted and can be redistributed and supported (with my own CSP headers and everything served from limited/restricted environments).

this solution and answer gleaned from: https://deno.land/manual/linking_to_external_code https://github.com/denoland/deno/issues/6491 notable: https://github.com/denoland/deno/security/advisories/GHSA-xpwj-7v8q-mcgj

Please note, SRI doesn't address the sources (whether a URL, CDN, npm, etc) prior to implementing this answer's solution, including transformations which occur along the way, whether within or between a project and delivery (eg via a CDN). Obviously reviewing incoming code and processing it appropriately is the only way to ensure what a given project then generates hashes for is what then follows as having a certain expectation of integrity.

Niggling answered 4/11, 2021 at 0:46 Comment(3)
This way, I think you have no CDN advantages anymore.Bourque
The advantage of the CDN and URLs generally is to not maintain and offload all concerns/work/risk relating to "package management"; whether with npm or not the sources, including the CDN and packages, are suspect at each point of the project, the transformations, the delivery. SRI only ensures what was seen when signed is changed or not after that point. It doesn't indicate trust before that point, only after it where and when the hashes match. This doesn't address any of those concerns in the answer (yet).Niggling
My previous comment assumes use of an CDN for my own applications and their respective assets. If @Bourque meant shared assets from unpkg.com or esm.sh my understanding is that the implementations of SRI are not complete or reliable. If you see otherwise please explain with relevant detail. Thanks.Niggling

© 2022 - 2024 — McMap. All rights reserved.