angular-cli: inlineCritical CSS asynchronous loading method breaks with CSP
Bug Report
Affected Package
Is this a regression?
Nay
Description
The method used for inlining critical css and asynchronously loading it, breaks and doesn’t load the external stylesheet when you have a content security policy that doesn’t include script-src 'unsafe-inline'
. As the name suggests this isn’t a very secure way of operating for various reasons such as script injection.
You can fix by disabling inlineCritical in the optimizations - however maybe there is a better way to load the styles, maybe in a JS file?
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">
It’s the onload="this.media='all'"
that breaks it
Minimal Reproduction
https://github.com/jpduckwo/ng12-csp-issue
run: ng serve
Exception or Error
Refused to execute inline event handler because it violates the following Content Security Policy directive: “default-src ‘self’”. Either the ‘unsafe-inline’ keyword, a hash (‘sha256-…’), or a nonce (‘nonce-…’) is required to enable inline execution. Note also that ‘script-src’ was not explicitly set, so ‘default-src’ is used as a fallback.
Your Environment
Angular Version:
@angular-devkit/architect 0.1200.1
@angular-devkit/build-angular 12.0.1
@angular-devkit/core 12.0.1
@angular-devkit/schematics 12.0.1
@schematics/angular 12.0.1
rxjs 6.6.7
typescript 4.2.4
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 101
- Comments: 36 (2 by maintainers)
Commits related to this issue
- fix: disable automatic critical css inlining for now https://github.com/angular/angular-cli/issues/20864 — committed to pascaliske/pascaliske.dev by pascaliske 3 years ago
- fix: style loading issues on build https://github.com/angular/angular-cli/issues/20864 — committed to AXeL-dev/anime-tracker by AXeL-dev 3 years ago
- feat(@nguniversal/common): disable critical CSS inlining by default With this change we disable critical css inline by default. The main motivations are the following issues https://github.com/angula... — committed to angular/universal by alan-agius4 3 years ago
- feat(@angular-devkit/build-angular): disable critical CSS inlining by default With this change we disable critical css inline by default. The main motivations are the following issues #20760 and #208... — committed to angular/angular-cli by alan-agius4 3 years ago
- Harden devconsole content security policy The changes to angular.json are to disable inline critical CSS, which breaks without "script-src 'unsafe-inline'" (which we obviously don't want to have). Se... — committed to accrescent/devconsole by lberrymage 2 years ago
Found how to disable the inlineCritical styles https://github.com/angular/angular-cli/commit/aa3ea885ed69cfde0914abae547e15d6d499a908 https://angular.io/guide/workspace-config#styles-optimization-options In angular.json in the build configuration Instead of
put
Just disable inline CSS.
External stylesheets worked for dial-up internet, IDK why people insist on complicating the most simple things when speeds are 100x what they used to be.
EDIT: Enjoy your bugs.
@alan-agius4 and maybe others…
What is the status of this issue? What i understand is that we have to disable the inline css feature, OR make our CSP more insecure. Both dont sound like great solutions.
Will there be a good fix to not require us to either sacrifice perf or security? If not, will there be documentation for angular with a CSP guide on how to create a Security performance balance?
We can probably solve this by changing the preload strategy of critters from
media
todefault
.Let me reach out to the Chrome team so see if there are any drawbacks of doing so.
@sander1095 One work-around is of course to add
script-src 'unsafe-hashes' 'sha256-MhtPZXr7+LpJUY5qtMutB+qWfQtMaPccfe7QXtCcEYc='
This is not really more insecure, as it only allows thethis.media='all'
script to execute, which is the culprit of this issue.The workaround from @tiberiuzuld fixed the issue for me.
It might be a good idea to set up an integration test with a restrictive CSP and a basic app generated from the cli. In this way this sort of issue could be caught early. Angular should be able to work with this:
@alan-agius4 another option would be to have the JS bundle itself flip those preloads to stylesheets (as a backup). It could scan for the preload/disabled
<link>
elements, add anonload
listener, then force a synchronous load event if the sheet has already been loaded:We were facing the same issue. We had 2 optimization flags. The first build.options.optimization was set to false. I initially changed the 2nd one with the production configuration. The workaround didn’t work. I then changed the first one (initially at false) to the workaround and it started working.
Not sure if it’s a bug, but I would expect optimization false to also implies inlineCritical:false. At least, it’s working now.
Hello, I’m on Angular 13.0.2 and I have the same problem as @pacocom. I tried to apply the fix to other config areas of my angular config (as suggested by @jpduckwo ) but still the same error. Any ideas?
An interesting article with an overview of this concept :
The necessary change, to actually skip the
onload
event listener in the ssr html is to provide the additionalinlineCriticalCss
property when using thengExpressEngine
:@rickz21 try putting the optimizations in the build > options portion of the JSON
e.g.
I also faced this issue. There are any options to disable this inline of critical css?
I also faced this.
https://0xdbe.github.io/AngularSecurity-DisableInlineCriticalCSS/
@AElmoznino that is the correct solution to restore the previous behaviour. However there may be multiple
"optimization": true
statements in your angular.json that you need to update. E.g. multiple configs for test / production etc.@alan-agius4 I think the “body” mode could work - it injects styles at the
<link>
location, then adds the<link rel=stylesheet href=css>
just before</body>
.