Add CSP nonce to <link rel="stylesheet"> tags when ngCspNonce is set
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 35/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Đình trệ
- Công nghệ
- angular, typescript
- Lĩnh vực
- build-system, security
Hướng nghiên cứu
Read packages/angular/build/src/utils/index-file/nonce.ts, especially addNonce, and the stylesheet-link generation in packages/angular/build/src/utils/index-file/augment-index-html.ts. Find the related nonce and index-file tests, then run them. Done means generated stylesheet links—including the noscript fallback and critical-CSS link—receive the configured nonce; an open linked pull request indicates work is already underway.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Command
build, serve
Description
When ngCspNonce is set in index.html, the build copies the nonce onto inline <style> tags and onto <script> tags, including <script src> since #27875. It does not copy it onto the <link rel="stylesheet"> tags generated for the global styles bundle.
So a strict, nonce-based CSP still has to allow 'self' (or a host source) in style-src / style-src-elem. Otherwise the browser blocks the global stylesheet:
Content-Security-Policy: The page's settings blocked a style (style-src-elem) at https://example.com/styles-RBGG4ONZ.css
This makes a nonce-only style-src impossible, even though everything else the build emits is already nonced: the critical inline <style>, the ngCspMedia loader <script>, main-*.js, and component styles added at runtime.
The responsible code is addNonce, which only handles style and script:
https://github.com/angular/angular-cli/blob/23e3d44a7f051cd3bb67700b8d8407f73b7aa7f3/packages/angular/build/src/utils/index-file/nonce.ts#L21-L41
The stylesheet links are generated here, without a nonce:
https://github.com/angular/angular-cli/blob/23e3d44a7f051cd3bb67700b8d8407f73b7aa7f3/packages/angular/build/src/utils/index-file/augment-index-html.ts#L186
Reproduction (@angular/build 22.1.0, @angular/build:application, production config with default inlineCritical):
index.html:<app-root ngCspNonce="RANDOM_NONCE"></app-root>ng build- Output
index.html:<style nonce="RANDOM_NONCE">/* critical css */</style> <script nonce="RANDOM_NONCE">/* ngCspMedia loader */</script> <link rel="stylesheet" href="styles-XXXX.css" media="print" ngcspmedia="all"> <noscript><link rel="stylesheet" href="styles-XXXX.css"></noscript> ... <script src="main-XXXX.js" type="module" nonce="RANDOM_NONCE"></script> - Serve with
Content-Security-Policy: style-src 'nonce-RANDOM_NONCE'; script-src 'nonce-RANDOM_NONCE'. The global stylesheet is blocked.
The nonce attribute is valid on <link rel="stylesheet">. The element's nonce is passed as the request's cryptographic nonce metadata and is checked against style-src-elem, and all current browsers support this. A hash source can't be used instead, because hashes don't match external stylesheets.
Why not just allow 'self'?
'self' is an allowlist covering the whole origin. A nonce only authorizes the elements the server put into this particular response. That gap matters for styles too:
'self'covers the whole origin, not the app. An Angular app is often served under a sub-path (<base href>,--base-href,APP_BASE_HREF) behind a reverse proxy, sharing its origin with other apps, an API, or file downloads. With'self', anytext/cssresponse anywhere on that origin counts as a trusted stylesheet. That includes legacy apps, user-uploaded files and endpoints that echo input. The page's security then depends on everything else deployed on the origin.- Injected markup can load stylesheets. Angular sanitizes bindings, but HTML can still get in through
bypassSecurityTrustHtml, server-side rendering of untrusted data, or third-party widgets. When scripts are blocked, injected CSS is the next tool for an attacker. With'self', injected markup can load any same-origin stylesheet. With a nonce-only policy, it can load none, because it can't know the per-response nonce. - What CSS injection can do. It can change the UI: hide or overlay elements, fake dialogs, phishing prompts, clickjacking-style redressing inside the page. Attribute-selector and
:has()tricks can probe the DOM for things like CSRF tokens or input values. Strictimg-src/font-srclimit exfiltration, but they don't stop the UI attacks. - Allowlists are the weak part of CSP. Research on real-world policies found that allowlist sources (including
'self') are routinely bypassable. The recommendation is nonce- or hash-based "strict CSP". See Weichselbaum et al., "CSP Is Dead, Long Live CSP!" (ACM CCS 2016), https://research.google/pubs/csp-is-dead-long-live-csp-on-the-insecurity-of-whitelists-and-the-future-of-content-security-policy/, and https://web.dev/articles/strict-csp. That work focuses on scripts, but the reasoning carries over to styles.
style-src 'self' is clearly less dangerous than script-src 'self'. This is a defense-in-depth request, and given everything else is already nonced it is a small, consistent change.
Describe the solution you'd like
In addNonce, also add the nonce to <link> tags whose rel contains stylesheet, including the <noscript> fallback and the media="print" / ngCspMedia link created by critical CSS inlining. A minimal change:
rewriter.on('startTag', (tag) => {
const isStylesheetLink =
tag.tagName === 'link' &&
tag.attrs.some((attr) => attr.name === 'rel' && attr.value.split(/\s+/).includes('stylesheet'));
if (
(tag.tagName === 'style' || tag.tagName === 'script' || isStylesheetLink) &&
!tag.attrs.some((attr) => attr.name === 'nonce')
) {
tag.attrs.push({ name: 'nonce', value: nonce });
}
rewriter.emitStartTag(tag);
});
It may also make sense to nonce <link rel="modulepreload"> (and rel="preload" as="script|style"), because those requests are also checked against script-src / style-src.
Describe alternatives you've considered
- Keep
'self'instyle-src. This works, but it trusts every same-origin stylesheet rather than only the ones the server emitted (see "Why not just allow'self'?" above). It is the only reason'self'is still needed. autoCsp. It generates hashes for inline scripts in a<meta>tag. It does not cover external stylesheets and does not work with a CSP sent as a response header.- Hash sources. Hashes don't match external stylesheets.
- Inline all global styles (e.g. move them into a
ViewEncapsulation.Noneroot component so they are added at runtime with the nonce). This loses the separately cached stylesheet and runs into the component style budgets. - Post-process
index.htmlafterng build(our current workaround). We use a build step that rewrites every<link rel="stylesheet"to<link rel="stylesheet" nonce="…"and fails the build if a link without a nonce is left over. It works, but every project with a strict CSP has to rebuild and maintain it. The application builder has no index-HTML transform hook where this could live.
- Ngôn ngữ chính
- TypeScript
- Star
- 27k
- Fork
- 11.8k
- Merge trung bình
- 1 ngày 2 phút
- Pull request đã merge (30 ngày)
- 168
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của angular/angular-cli
-
Can't use an array of hostnames in --allowedHosts cli parameter in @angular/build:dev-serverĐang mởarea: @angular/build gemini-triaged
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
angular/angular-cli#33955 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
area: @angular/cli gemini-triaged
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
angular/angular-cli#33055 · 1 bình luận · 3 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
dev-server: es2016 prebundle target for zone.js apps lowers private fields and breaks dependenciesĐang mởarea: @angular/build
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
angular/angular-cli#34280 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Follow-up to issue: SSR: Add a way to configure externalDependencies for the server bundle onlyĐang mởarea: @angular/build
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
angular/angular-cli#34262 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Vite dev server: proxy config normalization reorders glob keys, drops string `context` and misses URLs with a query stringCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mởarea: @angular/build
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 65/100
angular/angular-cli#34257 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của angular/angular-cli
Issue tương tự
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 82/100
siyuan-note/siyuan#20313 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 92/100
alunduil/projects-v2-sync#14 ·
-
Service process inherits the caller's cwd at first use, holding that folder open on Windows (EBUSY)Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
-
DevTools page styles leak into the host app in developmentCó thể đã có người làm @onmax đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
nuxt-modules/better-auth#567 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày