Subresource integrity
Integrity hashes in the import map and the preload links, so the browser refuses a module that was tampered with.
Enabling integrity#
Opt in globally, then control it per pin.
Integrity calculation is off until you call enable_integrity! in config/importmap.rb. With it on, integrity: true (the default for every pin) computes a hash for each local asset through the asset pipeline; integrity: "sha384-…" uses the value you give; integrity: false or nil turns it off for that pin.
# Enable integrity calculation globally
enable_integrity!
# With integrity enabled, these auto-calculate integrity hashes
pin "application" # auto-calculated
pin "admin", to: "admin.js" # auto-calculated
pin_all_from "app/javascript/controllers", under: "controllers" # auto-calculated
# Explicit control
pin "cdn_package", to: "https://cdn.example.com/cdn_package.js", integrity: "sha384-abc123..." # pre-calculated hash
pin "no_integrity_package", integrity: false # explicitly disabled
pin "nil_integrity_package", integrity: nil # explicitly disabledThis is particularly useful for local JavaScript files managed by the asset pipeline, for bulk pin_all_from pins where computing hashes by hand would be tedious, and in development where file contents change often. External CDN packages should provide their own hashes.
Propshaft#
SRI needs Propshaft 1.2+ and an integrity hash algorithm configured in your application; without it integrity is disabled by default under Propshaft. Sprockets has integrity support out of the box.
config.assets.integrity_hash_algorithm = "sha256" # or "sha384", "sha512"What the browser gets#
The hashes are included in the import map's integrity section and on each module preload link, and the browser validates them as it loads the modules:
{
"imports": {
"lodash": "https://ga.jspm.io/npm:[email protected]/lodash.js",
"application": "/assets/application-abc123.js",
"controllers/hello_controller": "/assets/controllers/hello_controller-def456.js"
},
"integrity": {
"https://ga.jspm.io/npm:[email protected]/lodash.js": "sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF",
"/assets/application-abc123.js": "sha256-xyz789...",
"/assets/controllers/hello_controller-def456.js": "sha256-uvw012..."
}
}<link rel="modulepreload" href="https://ga.jspm.io/npm:[email protected]/lodash.js" integrity="sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF">
<link rel="modulepreload" href="/assets/application-abc123.js" integrity="sha256-xyz789...">When bin/importmap rewrites a pin#
integrity: true and integrity: false are settings, and they survive a rewrite by pin, update or pristine the way preload: does. An explicit hash string belongs to one particular file, so it is dropped when the URL changes — the old hash would no longer match. Pin a fresh hash after an update of a remote pin that carried one.