Content Security Policies (CSPs) can help protect an application from Cross-Site Scripting (XSS) attacks by adding a security layer to help prevent unauthorized code from running in the browser.
In the world of Stencil applications, if your app leverages a restrictive CSP (i.e. anything other than 'unsafe-inline') and components without a Shadow DOM, you'll likely run into an error in the browser console stating the certain styles or scripts were not able to run because they violate the effective CSP.
To resolve this issue, we've implemented support for using CSP nonces in many of the Stencil output targets.
NOTE: CSPs and some CSP patterns are not supported by legacy browsers such as Internet Explorer. For a more detailed list, please see the CSP browser compatibility table.
How to Use a Nonce
The actual generation of the nonce value and enforcement of the correct CSP are not the responsibility of Stencil. Instead, the server the application is hosted on will need to generate the nonce value for each page view, construct the CSP, and then correctly handle passing the generated nonce to Stencil based on which output target is being consumed.
There are many resources available that walk through setting up a CSP and using the nonce behavior. This article walks through the process using Nginx and Webpack. Obviously, these resources don't account for the Stencil specifics, but we'll try to make those parts clear as we move on.
Per the MDN Guide on nonces, a nonce should be "a random base64-encoded string of at least 128 bits of data from a cryptographically secure random number generator".
Output Targets
Using nonces may differ slightly between output targets, so please be sure to use the correct pattern based on the context in which your Stencil components are consumed.
Dist
Consuming a nonce
in the
dist
output target is easy using the provided
setNonce
helper function. This function is exported from the index
file of the output target's designated output directory.
This function simply accepts the nonce
string value that you want set for every
style
and
script
tag.
This is an example of consuming the dist
output in an Angular app's entrypoint:
// main.ts
import { defineCustomElements, setNonce } from 'my-lib/loader';
// Will set the `nonce` attribute for all scripts/style tags
// i.e will run styleTag.setAttribute('nonce', 'r4nd0m')
// Obviously, you should use the nonce generated by your server
setNonce('r4nd0m');
// Generic Angular bootstrapping
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.log(err));
defineCustomElements();
Custom Elements
Consuming a
nonce
in the dist-custom-elements
output target is easy using the provided
setNonce
helper function. This function is exported
from the index file of the output target's designated output directory.
This function simply accepts the nonce
string value that you want set for every
style
and
script
tag.
This is an example of consuming the dist-custom-elements
output in an Angular app's entrypoint:
// main.ts
import { defineCustomElements, setNonce } from 'my-lib/dist/components';
// We'll assume we have `autoDefineCustomElements` enabled
import 'my-lib/dist/components/my-component';
// Will set the `nonce` attribute for all scripts/style tags
// i.e will run styleTag.setAttribute('nonce', 'r4nd0m')
// Obviously, you should use the nonce generated by your server
setNonce('r4nd0m');
// Generic Angular bootstrapping
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.log(err));
WWW
Unfortunately, setting nonce
attributes gets a bit trickier when it comes to
SSR and SSG. As a
nonce
needs
to be unique per page view, we cannot define/set the nonce at build time. So, this responsibility now falls on the
hydrate app's execution of runtime code.
SSR
Since there is not an easy way (or any way) of exposing and executing helper functions to manipulate the outcome of the runtime code, Stencil
has fallback behavior for pulling the
nonce
off of the window object.
So, for SSR, your app can simply set the desired nonce
value on
window.nonce
at bootstrap. Yes, this does involve some manual configuration
for the code served by your server.
This isn't a security risk because, for an attacker to execute a script to pull the nonce from the window object, they would have needed to know the nonce ahead of the script's execution.
SSG
We cannot support CSP nonce with SSG. Because all of the code is generated during
pre-rendering, we don't generate the style
or script
tags at runtime.
If an application wants to leverage nonces in SSG, they can build a mechanism to scrape the pre-rendered code and apply the attribute server-side before
it is served to the client.
Contributors
Thanks for your interest!
We just need some basic information so we can send the guide your way.