Add Subresource Integrity for JS and CSS files #9933
Conversation
if (item.sha512) { | ||
itemObj.hash = item.sha512; | ||
} else { | ||
const hash = createHash('sha512'); |
abernix
Jun 14, 2018
Member
If we were to update tools/isobuild/bundler.js
, here to also generate and store a sha512
into the manifest (thus ensuring that item.sha512
is defined above) — what would be the use-case for needing to generate this hash at run-time (on each request), as would be occurring here?
(It seems like this would be the way to go, but I'm curious if I'm missing something.)
If we were to update tools/isobuild/bundler.js
, here to also generate and store a sha512
into the manifest (thus ensuring that item.sha512
is defined above) — what would be the use-case for needing to generate this hash at run-time (on each request), as would be occurring here?
(It seems like this would be the way to go, but I'm curious if I'm missing something.)
morloy
Jun 15, 2018
Author
Contributor
As explained below, using SHA-512 for the hash in the manifest and truncating it for filenames would be a good approach in my opinion.
As explained below, using SHA-512 for the hash in the manifest and truncating it for filenames would be a good approach in my opinion.
@@ -19,7 +20,8 @@ export async function generateHTMLForArch(arch, includeHead) { | |||
cacheable: true, | |||
url: '/packages/templating-runtime.js?hash=c18de19afda6e9f0db7faf3d4382a4c953cabe18&v="1"', | |||
size: 24132, | |||
hash: 'c18de19afda6e9f0db7faf3d4382a4c953cabe18' | |||
hash: 'c18de19afda6e9f0db7faf3d4382a4c953cabe18', | |||
sha512: 'mJ9OEOi4HbVMzqiVGOjxCiIIImgnzEg6QhorPno46yULMu/3tKmENYriasWP9RrM+wLNwlTzw6+9d+nWY9/A0A==', |
abernix
Jun 14, 2018
Member
Perhaps using sri
would be a more appropriate name here, since the suggested algorithm might update over time? (And has already changed since when we first started discussing this — for example, SHA256 was no longer recommended by the NSA, if I recall)
Perhaps using sri
would be a more appropriate name here, since the suggested algorithm might update over time? (And has already changed since when we first started discussing this — for example, SHA256 was no longer recommended by the NSA, if I recall)
@@ -19,7 +20,8 @@ export async function generateHTMLForArch(arch, includeHead) { | |||
cacheable: true, | |||
url: '/packages/templating-runtime.js?hash=c18de19afda6e9f0db7faf3d4382a4c953cabe18&v="1"', | |||
size: 24132, | |||
hash: 'c18de19afda6e9f0db7faf3d4382a4c953cabe18' | |||
hash: 'c18de19afda6e9f0db7faf3d4382a4c953cabe18', |
abernix
Jun 14, 2018
Member
I had been tempted (both today and in the past) to suggest that we switch to using a newer cipher for hash
(which currently uses SHA-1, 40-bytes in length — in hex) and just use that for the SRI — relying on only a single hash! 🙌 🤔
That temptation was originally based on me thinking we could go with SHA-256, which has a relatively short hexadecimal representation (64-bytes) and would still work fine as a filename (which hash
is frequently used for in builds and when serving files to clients!).
Unfortunately, as hashes become more complex, they become longer and the hex representation of a SHA-512 hash would be 128-bytes — a super long filename. (Maybe too long?)
Most problematically though, SRI requires base64
encoding which, while shorter than 128-bytes for SHA-512, fails to be useful as a filename because of the base64 encoding (/
, in particular), so my temptation seems to be well-prevented.
I had been tempted (both today and in the past) to suggest that we switch to using a newer cipher for hash
(which currently uses SHA-1, 40-bytes in length — in hex) and just use that for the SRI — relying on only a single hash!
That temptation was originally based on me thinking we could go with SHA-256, which has a relatively short hexadecimal representation (64-bytes) and would still work fine as a filename (which hash
is frequently used for in builds and when serving files to clients!).
Unfortunately, as hashes become more complex, they become longer and the hex representation of a SHA-512 hash would be 128-bytes — a super long filename. (Maybe too long?)
Most problematically though, SRI requires base64
encoding which, while shorter than 128-bytes for SHA-512, fails to be useful as a filename because of the base64 encoding (/
, in particular), so my temptation seems to be well-prevented.
morloy
Jun 15, 2018
Author
Contributor
I‘d recommend switching to SHA-512 and simply truncate it for the filenames. This seems to be totally fine. If more information needs to go into the filename, there’s Base58 or UNMISTAKABLE_CHARS, that would give Base55.
Recoding the hash from hex to Base64 for SRI would be trivial.
I‘d recommend switching to SHA-512 and simply truncate it for the filenames. This seems to be totally fine. If more information needs to go into the filename, there’s Base58 or UNMISTAKABLE_CHARS, that would give Base55.
Recoding the hash from hex to Base64 for SRI would be trivial.
@@ -9,8 +9,9 @@ export const headTemplate = ({ | |||
}) => { | |||
var headSections = head.split(/<meteor-bundled-css[^<>]*>/, 2); | |||
var cssBundle = [...(css || []).map(file => | |||
template(' <link rel="stylesheet" type="text/css" class="__meteor-css__" href="<%- href %>">')({ | |||
template(' <link rel="stylesheet" type="text/css" class="__meteor-css__" href="<%- href %>" integrity="sha512-<%- hash %>" crossorigin="anonymous">')({ |
abernix
Jun 14, 2018
Member
I'm curious if this crossorigin="anonymous"
might potentially cause problems for existing deployments which might necessitate the presence of user credentials (as defined here; e.g. HTTP Auth, Cookies, etc.) in order to serve the JS or CSS files (let's say the Meteor's deployment was fully behind that forced HTTP Authentication).
From what I understand, it's mandatory for crossorigin
to be set for SRI to work (See 0, 1), but I'm afraid this might need to be toggleable between the valid options, anonymous
and use-credentials
.
Any thoughts?
I'm curious if this crossorigin="anonymous"
might potentially cause problems for existing deployments which might necessitate the presence of user credentials (as defined here; e.g. HTTP Auth, Cookies, etc.) in order to serve the JS or CSS files (let's say the Meteor's deployment was fully behind that forced HTTP Authentication).
From what I understand, it's mandatory for crossorigin
to be set for SRI to work (See 0, 1), but I'm afraid this might need to be toggleable between the valid options, anonymous
and use-credentials
.
Any thoughts?
morloy
Jun 15, 2018
Author
Contributor
Having this configurable sounds good to me. But where would this config go ideally? Any ideas?
Having this configurable sounds good to me. But where would this config go ideally? Any ideas?
@@ -54,8 +55,9 @@ export const closeTemplate = ({ | |||
'', | |||
|
|||
...(js || []).map(file => | |||
template(' <script type="text/javascript" src="<%- src %>"></script>')({ | |||
template(' <script type="text/javascript" src="<%- src %>" integrity="sha512-<%- hash %>" crossorigin="anonymous"></script>')({ |
abernix
Jun 14, 2018
Member
Same question as above!
Same question as above!
@abernix I added |
LGTM! |
Follow-up to #9933. As recommended by @abernix, the sha1 hash of every file is now computed from the file's sha512 hash, so we don't have to hash the entire contents of the file twice with two different algorithms. Other changes/improvements: * Invalidate the hashes when/if `File#setContents` is called. * Ignore `options.hash` and just compute hashes from actual file contents. Disagreement here would be worse than any performance benefits from precomputing the hash.
Follow-up to #9933. As recommended by @abernix, the sha1 hash of every file is now computed from the file's sha512 hash, so we don't have to hash the entire contents of the file twice with two different algorithms. Other changes/improvements: * Invalidate the hashes when/if `File#setContents` is called. * Ignore `options.hash` and just compute hashes from actual file contents. Disagreement here would be worse than any performance benefits from precomputing the hash.
Since recent changes in these packages (#9933) depend on changes to the build tool, it seems wise to tie these updates to Meteor 1.7.1.
After moving our Meteor assets out to a CDN, the Mozilla Observatory kept complaining about missing Subresouce Integrity in our JavaScript files.
This PR adds a
sha512
hash to all basic Meteor files served to the client.Solves meteor/meteor-feature-requests#35.