We’ve recently come around to lifting the applications in my current project to Angular 16. One of the things the new version brings is support for nonces in inline styles.
As a short reminder, the styles your components define get included as
<style> tags in the
<head> of your application dynamically at runtime. This enables the style separation Angular provides (take a peek in your browser’s DevTools to see what I mean).
Until now, this meant that – if you were using a Content Security Policy (CSP) – you had to allow
unsafe-inline as a
style-src, which potentially leaves you open to injections.
With the advent of Angular 16, you can now make the Angular runtime add a
nonce attribute to each of the
<style> Tags, so you can limit the CSP to
style-src: 'self' 'nonce-$VALUE'; and have the browser reject each
<style> that does not have that nonce value.
In production, our application gets built into the usual bunch of static HTML and JS files and then packaged into a Docker container that has nginx as the web server. We also provide our own configuration for nginx during the Docker build.
Both aspects are important for the setup I describe below; if your setup looks different, you will have to see what you have to adapt. Since the nonce is something that is added at runtime but (as the name nonce – number used once implies) needs to be unique for each time the HTML document of the application is served, we need to be able to process that HTML document each time.
Changes in the application
index.html looks roughly like this
Note that in this example, we’ve also included the CSP as a
<meta> Element to the document. This way it also gets picked up when you run the built-in development server, but of course you can also have your nginx add the CSP as an HTTP header.
To include the nonce, you have to do two things:
- Add a
nonce-$VALUEsetting to the
style-srcsection of the CSP.
- Add an
ngCspNonceattribute to the root element of your application (this is where the Angular Runtime picks it up). You have to set the content of this attribute to the same
$VALUEyou use in the CSP.
We’ll let nginx take care of replacing these and just set them to the same value, so our
index.html becomes this:
Changes in the nginx configuration
To have nginx make the necessary replacements, we’ll use the
http_sub module. For it to do its work properly, we have to add two settings to our configuration. They could go into a
location block, but they can also go within the
server block and then apply globally.
The things happening here are
sub_filter_once offmakes nginx look for multiple occurrences of the strings to replace. Since we have
random_nonce_valuetwice in our
index.htmlwe need that.
sub_filterline declares which string needs to be replaced and by what.
$request_idis a value per request that nginx creates anyway, so it comes in handy here.
Conclusion and Caveats
Put all this together, and your
index.html will declare a new nonce every time the root document of the application is served. This value is picked up by Angular, so any
style tag it creates conforms to the CSP and is used as before.
There are some things to keep in mind, though:
$request_idis unique per request, it is not a cryptographically secure ID, so it might be possible to predict values. As usual for security topics, you need to decide for yourself if that is secure enough for your environment.
- This setup covers the core Angular functionality. If you use additional frameworks that also add
<style>tags dynamically, you might need additional configuration of even scripting to ensure that their styles still work.
- Some of the value of the nonce comes from its one time use and its ephemerality. If your users have your application open for a long span of time, that might be counteracted.
So while adding nonces for styles is not a silver bullet, I think it is a relatively easy way to improve on your existing CSP and secure your application a bit better.