waysact / webpack-subresource-integrity

Webpack plugin for enabling Subresource Integrity.
MIT License
357 stars 46 forks source link

Invalid hashes for dynamic chunks #143

Closed egorovli closed 3 years ago

egorovli commented 3 years ago

Hi. It seems like the hashes generated by webpack-subresource-integrity are invalid for dynamic chunks, or at least they are in my case. I've set up a demo repository here: egorovli/webpack-subresource-intergrity-invalid-hashes-demo.

Errors are reported by Chrome and Safari:

[Safari] Cannot load script http://localhost:8080/js/pages-index.567515a1.js. Failed integrity metadata check. Content length: 240, Expected content length: 240, Expected metadata: sha256-MHGm+s4Q+K2a8lRQXGmNtKM1dRJ/43k5coDwriwaQao= sha384-qnwe2sIE1tMmTrzru4u+aQnGILMNvRY3Jzy7VFYe4nXcj/nCvhDmIx/MdX1yx40b sha512-zjgiy/5yoQlUYaeI9aGSMHzH2SWLgxzBv2Ap+bsHfNE+mGTkQPXj9jPErqGBp1fETg32EkyL2QT3A+hs1NlaNA==
[Chrome] Failed to find a valid digest in the 'integrity' attribute for resource 'http://localhost:8080/js/pages-index.567515a1.js' with computed SHA-256 integrity 'XGP3uuBStGvNx9WhKx/oY7quoKnT8bCRp6Lo+xrxvRc='. The resource has been blocked.

So the generated SHA-256 hash is MHGm+s4Q+K2a8lRQXGmNtKM1dRJ/43k5coDwriwaQao= and the computed one is XGP3uuBStGvNx9WhKx/oY7quoKnT8bCRp6Lo+xrxvRc=. For reference, I've also used webpack-assets-manifest plugin, which seems to give the correct hash:

{
  // ...
  "pages-index.js": {
    "src": "js/pages-index.567515a1.js",
    "integrity": "sha256-XGP3uuBStGvNx9WhKx/oY7quoKnT8bCRp6Lo+xrxvRc= sha384-jqbng205OanAGkvELOriKzfQhJYyzJBne5jGGTzirirm96YD9JatQCFXP7aQev+1 sha512-hPLfYrwHtYgHimUgvhrNjuAYuHoh/9Hz3HuuDpIs1juAeTy/ov1KGHxtclJefrvIcdKokQ1n4e5Xn9ZZmUyy/A=="
  },
  // ...
}

I've tried changing webpack's magic comments for the imported chunks and removing them entirely. I've also tried changing optimization.chunkIds to named with no effect.

Relevant package versions:

"html-webpack-plugin": "^4.5.1",
"webpack": "^5.12.3",
"webpack-assets-manifest": "^5.0.0",
"webpack-subresource-integrity": "^1.5.2"
jscheid commented 3 years ago

Hi, thanks for the repro.

I suspect this is because of Webpack 5's new "real" content hash.

I have a version 2.0 in the making that I will release an alpha version for soon, pretty sure that it will work with that but I'll add a test case to make sure. Stay tuned.

jmrossy commented 3 years ago

@jscheid I've just tested this plugin with the latest webpack 5 + HtmlWebpackPlugin and I'm finding that even the main bundle's hash is incorrect, not just the dynamic imports. Any suggestions on things to try?

See here for webpack config: https://github.com/celo-tools/celo-web-wallet/blob/rossy/sri/webpack.config.js To repro, try yarn && yarn build:prod then see the incorrect hash in dist/index.html (note you'll need to be on branch rossy/sri)

jscheid commented 3 years ago

If by latest HtmlWebpackPlugin you mean 5.0.0-beta.4, we're not supporting that yet. Support is coming in 2.0 which isn't far off, sit tight.

jmrossy commented 3 years ago

@jscheid I do mean 5.0.0-beta.4, thanks for clarifying! Ok will do.

jmrossy commented 3 years ago

@jscheid Sorry to bother you here again, just looking for a rough estimate of when 2.0's alpha would be available? A few days or a few weeks? Knowing the rough timeline would help decide if it's worth it to wait or workaround for now.

jscheid commented 3 years ago

Probably not more than a few days.

trickeyone commented 3 years ago

I'm actually seeing this issue with webpack 4. We weren't previously using SRI but decided to enable it. The hashes being generated are incorrect. It doesn't seem to actually be something with this plugin, though. I created a temporary plugin to test my theory. My plugin reads the files on the done webpack hook, then generates the hashes of the file content.

const contents = fs.readFileSync(path.resolve(outputPath, publicPath), {encoding: 'utf8', flag: 'r'});
const hash = crypto.createHash(algo);
hash.update(contents);

return `${algo}-${hash.digest('base64')}`;

I also logged the hashed values out.

<i> [SymfonyAngularEntrypoints] Asset Integrity: /js/runtime.bf08a7f3.js [sha256-rd0TLNSQ1E7FxBYhWpQ0jxZJqnvnp6QKYyTRtr9W3Rc= sha384-P/Ig4JIM1Ct+eyHLw7pVdQxsxsX3QjatDlubQWyRi7qXXSf8nd9GxPGLmOiCwbxF sha512-nLmOZvKXa8kOklasXG2KynVbG3FkpY3/0CWB4dS37XclFDU0NNkMB5VeNVoqn9W41kRZjBS8XNEkAZadR5ckkA==]

But when I go to check the hash with shasum, the value is different. I can confirm that the value coming back from shasum is the same that Chrome is expecting.

$ shasum -a 256 js/runtime.bf08a7f3.js | awk '{ print $1 }' | xxd -r -p | base64
sGw18g5JtXCy6FLnPkt5gmEw9Lq2WP/IoL3/Cfdg0Q8=
$ cat js/runtime.bf08a7f3.js| openssl dgst -sha256 -binary | openssl base64 -A 
sGw18g5JtXCy6FLnPkt5gmEw9Lq2WP/IoL3/Cfdg0Q8=

The weirdest thing is that I created a test script that does the hashing after everything is complete and it's giving the correct hash.

const fs = require('fs');
const crypto = require('crypto');

const outputPath = '/path/to/webroot';
const fileName = '/js/runtime.bf08a7f3.js';
const contents = fs.readFileSync(outputPath + fileName);
const hash = crypto.createHash('sha256');
hash.update(contents, 'utf8');
console.log(hash.digest('base64'));
$ node test.js                                                                  
sGw18g5JtXCy6FLnPkt5gmEw9Lq2WP/IoL3/Cfdg0Q8=

So, I'm at a complete loss as to what's going on. Other than it appears that something in the webpack build is doing a final modification to the file contents after the SRI hash generation takes places. Which is odd since my plugin is the last plugin defined, which means that nothing should be occurring afterward.

Any thoughts would be greatly appreciated, I've been banging my head against the wall on this one for days now.

trickeyone commented 3 years ago

To add to the above, I just did another build in further troubleshooting. An error occurred during the afterEmit hook resulting in the build halting, but after the files had been written and after the SRI hashes had been calculated. When I ran shasum again, the hashes matched. So it appears that something is occurring during (possibly) the afterEmit.

jscheid commented 3 years ago

@trickeyone can you open a separate issue for this? This issue is about Webpack 5 (you're using 4) and about dynamic chunks (yours is failing at the top level).

However, if something is changing assets afterEmit for you then I don't think we can do much about it. You might want to check any other plugins you're using and if they do make changes afterEmit, see if you can get the author to change that. I'm happy to take a look as well but we will need some kind of way of reproducing this, ideally a demo repository.

trickeyone commented 3 years ago

@jscheid Ah, ok. I thought it was focusing on the hashes, my apologies. I just wanted to bring attention to that I was having an issue with webpack 4, as well.

jscheid commented 3 years ago

I've now released version 5.0.0-alpha.1, see https://github.com/waysact/webpack-subresource-integrity/issues/144

@egorovli I've tested your sample repo with the new version and seems to work fine, see here: https://github.com/jscheid/webpack-subresource-intergrity-invalid-hashes-demo/commit/c86a681a5b132e23922bcb5860ca501291497da2

Closing this, feel free to reopen if there's still a problem.