Astro-Shield
Introduction
This library will help you to compute the subresource integrity hashes for your
JS scripts and CSS stylesheets.
It works by installing an Astro hook that runs once the build step is done. This
hook performs 3 steps:
- Computes the Subresource Integrity hashes for your scripts and styles.
- Modifies the generated HTML to include the integrity hashes.
- In case you specified a filepath for your SRI hashes module, it will generate
(or update) a module that exports the associated SRI hashes, so you can use
them later for other purposes, such as configuring your
Content-Security-Policy
headers.
How to install
npm install --save-dev @kindspells/astro-shield
yarn add --dev @kindspells/astro-shield
pnpm add --save-dev @kindspells/astro-shield
How to use
In your astro.config.mjs
file:
import { resolve } from 'node:path'
import { defineConfig } from 'astro/config'
import { shield } from '@kindspells/astro-shield'
const rootDir = new URL('.', import.meta.url).pathname
export default defineConfig({
integrations: [
shield({
sri: {
enableStatic: true,
enableMiddleware: false,
hashesModule: resolve(rootDir, 'src', 'utils', 'sriHashes.mjs'),
scriptsAllowListUrls: [
'https://code.jquery.com/jquery-3.7.1.slim.min.js',
],
stylesAllowListUrls: [
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css',
],
allowInlineStyles: 'all',
allowInlineScript: 'all',
},
securityHeaders: {
contentSecurityPolicy: {
cspDirectives: {
'default-src': "'none'",
}
}
}
})
]
})
You can enable automated CSP headers generation by setting the option
securityHeaders.contentSecurityPolicy
(it can be an empty object if you don't
need to customise any specific behavior, but it must be defined).
Besides enabling CSP, you can also configure its directives to some extent, via
the cspDirectives
option.
[!IMPORTANT]
It is advisable to set the option sriHashesModule
in case your dynamic pages
include static JS or CSS resources.
Also, do not explicitly disable the enableStatic_SRI
option if you want
support for those static assets).
Accessing metadata generated at build time
Once you run astro build
, @kindspells/astro-shield
will analyse the static
output and generate a new module that exports the SRI hashes, so you can use
them in your CSP headers.
Here you can see an example of how the generated module looks:
export const inlineScriptHashes = ([])
export const inlineStyleHashes = ([
'sha256-VC84dQdO3Mo7nZIRaNTJgrqPQ0foHI8gdp/DS+e9/lk=',
])
export const extScriptHashes = ([
'sha256-+aSouJX5t2z1jleTbCvA9DS7+ag/F4e4ZpB/adun4Sg=',
])
export const extStyleHashes = ([
'sha256-iwd3GNfA+kImEozakD3ZZQSZ8VVb3MFBOhJH6dEMnDE=',
])
export const perPageSriHashes =
({
'index.html': {
scripts: [
'sha256-+aSouJX5t2z1jleTbCvA9DS7+ag/F4e4ZpB/adun4Sg=',
],
styles: [
'sha256-VC84dQdO3Mo7nZIRaNTJgrqPQ0foHI8gdp/DS+e9/lk='
],
},
'about.html': {
scripts: [
'sha256-+aSouJX5t2z1jleTbCvA9DS7+ag/F4e4ZpB/adun4Sg=',
],
styles: [
'sha256-iwd3GNfA+kImEozakD3ZZQSZ8VVb3MFBOhJH6dEMnDE=',
],
},
})
[!IMPORTANT]
If your website is very small or it relies on
View Transitions,
then it's best to rely on the inlineScriptHashes
, inlineStyleHashes
,
extScriptHashes
and extStyleHashes
values.
[!IMPORTANT]
If you don't rely on View Transitions and you care about minimising the size
of your CSP headers, then you can rely on the perPageSriHashes
exported
value.
Known limitations
-
⚠️ In case your SSR (dynamic) pages refer to static .js
or .css
files, and
any of these resources change, then you will need to run the astro build
command two consecutive times (Astro-Shield will emit a warning message
telling you about it).
-
The SRI hashes will be regenerated only when running astro build
. This means
that if you need them to be up to date when you run astro dev
, then you will
have to manually run astro build
.
-
In the context of Content-Security-Policy: When a script is loaded with a
static import rather than directly included with a <script>
tag, having
its hash present in the script-src
directive is not enough to ensure that
the browser will accept it.
This means that, for now, it is advisable to add 'self'
to the script-src
directive (adding 'strict-dynamic'
does not help either).
Some guarantees for peace of mind
Astro generates files in a very deterministic way, which means that for both JS
and CSS files:
- Their pseudo-random names are stable across different builds
- The files' contents do not change from build to build (unless, of course, we
change them on purpose), so their hashes are stable as well (this is nice
for hot reloading, which does not trigger the logic of this integration).
Other Relevant Guidelines
Main Contributors
This library has been created and is being maintained by
KindSpells Labs.
License
This library is released under MIT License.