Reputation: 175
I have a Spring boot 3.4 Webflux App. I need do configure CSP with dynamic nonce Here my code
My securityChain
@Bean public SecurityWebFilterChain otherSecurityWebFilterChain(final ServerHttpSecurity http) {
http
.securityMatcher(ServerWebExchangeMatchers.pathMatchers("/**"))
.authorizeExchange(auth -> auth.anyExchange().permitAll())
.headers(headers -> headers
.contentSecurityPolicy(csp ->
csp.policyDirectives("base-uri 'self'; form-action 'self'; script-src 'nonce-{nonce}' 'strict-dynamic' https:; object-src 'self';")
)
)
.addFilterBefore(new CSPFilter(), SecurityWebFiltersOrder.HTTP_HEADERS_WRITER);
return http.build();
}
My WebFilter
public class CSPFilter implements WebFilter {
@Override
public Mono<Void> filter(final ServerWebExchange exchange, final WebFilterChain chain) {
final var request = exchange.getRequest();
final var response = exchange.getResponse();
final var nonce = this.generateNonce();
final var decoratedResponse = new NonceResponseDecorator(response, nonce);
request.getAttributes().put("nonce", nonce);
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
private String generateNonce() {
final var nonceArray = new byte[64];
new SecureRandom().nextBytes(nonceArray);
return Base64.getEncoder().encodeToString(nonceArray);
}
}
My Wrapper
class NonceResponseDecorator extends ServerHttpResponseDecorator {
private final String nonce;
public NonceResponseDecorator(final ServerHttpResponse delegate, final String nonce) {
super(delegate);
this.nonce = nonce;
}
private String getHeaderValue(final String name, final String value) {
if (name.equals("Content-Security-Policy") && StringUtils.hasText(value)) {
return value.replace("{nonce}", this.nonce);
} else {
return value;
}
}
@Override
public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
this.setCspHeader();
return super.writeWith(body);
}
@Override
public Mono<Void> writeAndFlushWith(final Publisher<? extends Publisher<? extends DataBuffer>> body) {
this.setCspHeader();
return super.writeAndFlushWith(body);
}
private void setCspHeader() {
final HttpHeaders headers = this.getDelegate().getHeaders();
final String cspHeaderValue = headers.getFirst("Content-Security-Policy");
if (cspHeaderValue != null) {
headers.set("Content-Security-Policy", this.getHeaderValue("Content-Security-Policy", cspHeaderValue));
}
}
}
But in my filter the CSP header is never set. I miss something but cannot find out what
Upvotes: 1
Views: 10