Skip to content

Conversation

@ziqin
Copy link
Contributor

@ziqin ziqin commented Jan 14, 2026

This PR implements gh-10826.

Introduction

When strict Content Security Policy is used, web browsers block inline <script> or <style> blocks in HTML to mitigate XSS attacks injecting malicious inline blocks. To allow intended inline blocks, web developers can generate a hard-to-guess nonce and specify it in both the CSP and allowed inline blocks.

Currently, Spring Security only supports specifying static content security policy directives. This PR introduces support for dynamically generating a secure random nonce for CSP.

Implementation

A nonce is written to the CSP header in 2 steps:

  1. NonceGeneratingFilter / NonceGeneratingWebFilter generates a nonce and set it as a request attribute named _csp_nonce;
  2. ContentSecurityPolicyHeaderWriter / ContentSecurityPolicyServerHttpHeadersWriter reads the _csp_nonce attribute and write it to the CSP header, replacing the {nonce} placeholder in the configured policyDirectives.

Note:

  • The whole process is separated in 2 steps because by default a header writer cannot set a request attribute visible to views rendering the nonce in HTML.
  • _csp_nonce is chosen as the default attribute name because it has a similar format with the existing _csrf attribute. The attribute name is configurable.

Configuration

This PR also adds configurers for set up nonce-based CSP with Java/Kotlin lambda DSL, including the ability to specify a request matcher to determine whether a request requires CSP protection.

@Bean
SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .headers((headers) -> headers
            .contentSecurityPolicy((csp) -> csp
                .policyDirectives("default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self' 'nonce-{nonce}'")
                .nonceAttributeName("_csp_nonce")
                .requireCspMatchers("/admin/**", "/user/**")
            )
        );
    return http.build();
}

The ability to enable CSP conditionally is useful especially when the CSP directives contain a nonce, because the HTTP header value becomes dynamic and may change the cacheability of static asserts protected by Spring Security.

The conditional enabling of CSP protection is implemented in spring-security-config by wrapping the ContentSecurityPolicyHeaderWriter / ContentSecurityPolicyServerHttpHeadersWriter with the existing DelegatingRequestMatcherHeaderWriter / ServerWebExchangeDelegatingServerHttpHeadersWriter. The _csp_nonce attribute is generated unconditionally if nonce-based CSP is configured.

Breaking changes in corner case

In commit 8320c76 I changed the return type of 2 public configuration APIs:

  • ContentSecurityPolicySpec#reportOnly(boolean)
  • ContentSecurityPolicySpec#policyDirectives(String)

The return type is changed from HeaderSpec to ContentSecurityPolicySpec to allow method chaining.

I believe this API change was missed during the migration to lambda DSL and think it's small enough to be updated when releasing v7.1, but if 100% API stability is required, we could simply revert that commit, or introduce new APIs and deprecate the old ones.

Signed-off-by: Ziqin Wang <ziqin@wangziqin.net>
ziqin added 2 commits January 15, 2026 12:01
When strict Content Security Policy is used, web browsers block inline
<script> or <style> blocks in HTML to mitigate XSS attacks injecting
malicious inline blocks. To allow intended inline blocks, web developers
can generate a hard-to-guess nonce and specify it in both the CSP and
allowed inline blocks.

Currently, Spring Security only supports specifying static content
security policy directives. This commit adds support for dynamically
generating a secure random nonce for CSP:

- NonceGeneratingFilter & NonceGeneratingWebFilter are added to
  generate a nonce and set it as a request attribute,
- ContentSecurityPolicyHeaderWriter &
  ContentSecurityPolicyServerHttpHeadersWriter are modified to read
  the _csp_nonce attribute and write it to the Content-Security-Policy
  header, replacing the {nonce} placeholder in the given
  policyDirectives string.

The whole process is separated in two steps because by default a header
writer cannot set a request attribute visible to views for rendering the
nonce in HTML.

`_csp_nonce` is chosen as the default attribute name because it has a
similar format with the existing `_csrf` attribute. The attribute name
is configurable.

This commit implements spring-projectsgh-10826.

Signed-off-by: Ziqin Wang <ziqin@wangziqin.net>
This commit adds support for configuring nonce-based CSP with
Java lambda or Kotlin DSL.

By default, Spring Security adds protection headers for all served
resources. This was not a problem in the past because header values
were static. However, with the introduction of a dynamic nonce in
the CSP, the caching property of static asserts served may change.
With this in mind, this commit also adds convenient methods to set
a request matcher to determine whether a request requires CSP
protection or not.

Closes spring-projectsgh-10826

Signed-off-by: Ziqin Wang <ziqin@wangziqin.net>
Signed-off-by: Ziqin Wang <ziqin@wangziqin.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants