3

I am working on a Angular 18 project. My org has the strict CSP policy and no unsafe-inline is allowed. As documented by Angular here, while rendering the index.html, I generate a nonce per request when my server renders index.html and update it in <app-root ngCspNonce="">. This nonce, apparently, is then propagated by Angular in all the <scripts> or <styles> created at run time.

However, there are many styles (precisely 3 tags and quite huge) generated by Angular (even with inlineCritical: false) at build time. These are static in the dist generated by Angular at build time itself. It seems that the ngCspNonce attribute has no impact on these style. As a result, these style mark CSP violation at runtime.

Interestingly, if I keep a static ngCspNonce beforehand in my code, then at the build time that nonce is also copied to injected styles in the generated dist. However, that is of no use, as the nonce is supposed to be generated by server per request.

My Questions:

  1. Is it correct that ngCspNonce do not apply to these static build time styles. My research says Yes. If so how are they propagated when the nonce is present at build time. Why Angular do not add/update nonce to those tags at runtime like it does for other dynamically generated tags (As per the documentation)
  2. What is the way to handle these build time style tags (or script tags if angular might generate any) against the CSP. Should I be rewriting these tags again at every request.

As I said these styles are huge, generating a one time hash also doesn't seem feasible.

Would like to hear the thoughts on how this is being done for enterprise apps out there.

P.S: To my surprise, I am writing nonces in 3 tags from my server. All three of them land in the browser with the nonce included. Still, one of them is flagged for CSP violation. I am not sure, what might be the reason.

2
  • this is a great question and I have yet to see an answer that lays out how to implement nonces per-request in Angular. Can you include the code where you set ngCspNonce per request? (I've never understood how that is done either...) Commented Sep 4 at 16:56
  • 1
    That depends upon how you serve index.html. Basically your web server should write the index.html every time or perhaps modify a static version of it to include the new nonce. At present, my Angular dist is served through a AWS Cloudfront distribution, so I am doing the nonce generation and appending in a Lambda. Previously, while serving from spring boot, I used to fetch a static html file, open a stream on it and change the body at required places to append the nonce per request. Commented Sep 5 at 11:43

1 Answer 1

1

Well, this took me sometime but finally I have been able to figure out few concepts about Angular production handling of CSP and made my app CSP complaint without `unsafe-inline` using a nonce per request based approach.

In short, when you run Angular for production build with full optimization features (no inlineCritical:false), Angular does the following:

  1. Generates few inline `<style>` tags and host them at the top of the html page.

  2. Generate a <link> tag for loading styles at run time in an async manner to minimise FOUC.

Now, the first point will cause you CSP issues due to inline-style violation. What is interesting is, the second point will throw a script-src violation.

This is what was the original pain point for me. No visibile <script tag or inline scripts handler, still the script-src violation.

The reason for this is that the <link> in the second point contains an onload handler. This is a clever approach from Angular to defer the style parsing and thus minimize FOUC with CSS still being non-blocking. So yes, we do have a script violation taking place here.

The Angular team is well aware of it and they ship a IIFE to handle this. The IIFE changes the <link> tag attributes to activate the CSS rendering and unregister the onload event, once the CSS is activated.

Now the solution:

The Angular defined way to handle the CSP violation is to use a ngCspNOnce at the root element. This automatically triggers the nonce wherever required to handle the CSP (including the IIFE script). The only problem was to renew the nonce at the runtime.

To handle this: I started with a dummy nonce at the build time in my code. Angular auto propagates the nonce to handle CSP violation. During runtime, while serving each request, I wrote a simple logic to find the dummy nonce in my index.html file and replace it with a new cryptic nonce. This keeps the browser happy as far as CSP is considered.

There is obviously a performance hit since the index.html file now can't be cached and has to be regenerated every time (ideally not regenerated from scratch, just a string replacement of nonce is required). However all the other .js , .css and other assets can still be cached. The effective performance is still at par.

Hope this helps some one.

P.S: You can totally avoid the trouble caused by the second point if you opt for inlineCritical:false in your build configuration. For a small size application, this should be totally fine.

Sign up to request clarification or add additional context in comments.

3 Comments

if you can, can you include back-end and front-end code bits for getting/setting the nonce(s)? If there's a function/service you can call to get the nonce from an endpoint or something, is it also possible for xss to retrieve it? (I assume it could somehow, but it would be pretty difficult?... and so fairly easy to defend against? Are there any concerns there?)
Generating a nonce is just your custom logic. It is just a alphanumeric string, the random the better. For setting it as I mentioned, just use ngCspNonce in your app-root with a static value and then replace it with randomly generated nonce. For the XSS part, it is not possible to retrive it via XSS. The browser just validates the nonce while loading the page initially. If passed, the browser continue loading scripts, styles etc and hides the nonce from DOM. Therefore the DOM is just required to be present at load time. It doesnt matter much after that.
I'm curious about the backend code that modifies the intial static index.html file. (or does it generate it without having a static file in the first place?) How it deals with updates to linked scripts, etc... (I think my script filenames change per build for instance...)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.