35

Given code like this:

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

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

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
jens1o
  • 625
  • 7
  • 14
  • 2
    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.... :/ – jens1o Aug 21 '17 at 20:53
  • 3
    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. – loganfsmyth Aug 21 '17 at 21:20
  • Have you tried setting the `integrity` attribute on a ` – Noah Freitas Aug 21 '17 at 21:26
  • 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. – jens1o Aug 21 '17 at 22:23
  • 1
    @jens1o, it looks like there's some discussion of having this be based on a `` with an integrity attribute: https://github.com/w3c/webappsec-subresource-integrity/issues/26 and https://github.com/w3c/webappsec-subresource-integrity/issues/70 – Noah Freitas Aug 21 '17 at 23:08
  • 1
    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. – loganfsmyth Aug 22 '17 at 00:28

5 Answers5

8

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/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>

The same snippet without the integrity check:

// 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>

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

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • 1
    Note that this only works in Chrome. I just tested it in Safari 16 and Firefox 105 and the first example loads without errors. – fregante Sep 22 '22 at 08:23
  • 1
    @fregante yes, "which is unfortunately currently supported only in Blink browsers.". – Kaiido Sep 22 '22 at 08:33
5

You have to also define the module via

<script type="module" integrity="..." src="https://unpkg.com/redom@3.2.1/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.

fregante
  • 29,050
  • 14
  • 119
  • 159
  • 2
    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. – fuzzyTew Apr 09 '20 at 09:23
  • 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. – fregante Apr 09 '20 at 15:50
  • 1
    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. – fuzzyTew Apr 10 '20 at 16:14
  • 3
    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 ` – Dan Fabulich Dec 01 '20 at 05:23
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.

Brett Zamir
  • 14,034
  • 6
  • 54
  • 77
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.

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.

jimmont
  • 2,304
  • 1
  • 27
  • 29
  • This way, I think you have no CDN advantages anymore. – jens1o Nov 10 '21 at 17:33
  • 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). – jimmont Nov 11 '21 at 15:15
  • 1
    My previous comment assumes use of an CDN for my own applications and their respective assets. If @jens1o 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. – jimmont Nov 21 '21 at 17:11