CSP für Spring Boot konfigurieren
Content Security Policy in Spring Boot einrichten -- mit SecurityFilterChain, dynamischen Nonces per OncePerRequestFilter und Thymeleaf-Integration.
Content Security Policy in Spring Boot
Content Security Policy (CSP) ist der wichtigste HTTP-Security-Header gegen Cross-Site Scripting (XSS). Er definiert, welche Ressourcen der Browser laden darf, und blockiert alles andere. CSP ist mit 35 von 166 Punkten der einflussreichste Header im Wolf-Agents Web Security Check.
Spring Security setzt CSP nicht automatisch. Sie müssen den Header explizit in der
SecurityFilterChain konfigurieren. Für statische Policies reicht die contentSecurityPolicy()-DSL.
Für dynamische Nonces benoetigen Sie einen OncePerRequestFilter, der pro Request
einen Nonce generiert und als Request-Attribut für Thymeleaf verfügbar macht.
CSP im Report-Only-Modus starten
Beginnen Sie immer im Report-Only-Modus. Spring Security bietet dafür keine eigene DSL --
verwenden Sie stattdessen den Header-Namen Content-Security-Policy-Report-Only direkt.
Der Browser meldet Verstoesse in der Konsole, blockiert aber keine Ressourcen.
// SecurityConfig.java -- Report-Only CSP
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; "
+ "script-src 'self'; "
+ "style-src 'self' 'unsafe-inline'; "
+ "img-src 'self' data: https:; "
+ "font-src 'self'; "
+ "connect-src 'self'; "
+ "object-src 'none'; "
+ "frame-ancestors 'self'; "
+ "base-uri 'self'; "
+ "form-action 'self'")
)
);
return http.build();
} Verwenden Sie @Profile("prod") für die Enforcement-Konfiguration und @Profile("dev") für eine lockere CSP. So können Sie lokal mit 'unsafe-inline' entwickeln, während Produktion strikte Nonces erzwingt.
Violations analysieren
Oeffnen Sie die Browser DevTools (F12) und prüfen Sie die Console. CSP-Violations erscheinen als Warnungen mit der blockierten Ressource und der verletzten Direktive.
| Violation | Loesung |
|---|---|
| Thymeleaf Inline-Script blockiert | Nonce per th:attr="nonce=${cspNonce}" hinzufuegen |
| Webjars-Ressourcen blockiert | /webjars/**-Pfad in script-src erlauben oder Nonces nutzen |
| Spring Boot DevTools blockiert | LiveReload-Port in connect-src erlauben (nur Dev-Profil) |
| Actuator-Endpoint blockiert | Separate CSP für /actuator/** mit eigener SecurityFilterChain |
Nonces mit OncePerRequestFilter
Für maximale Sicherheit verwenden Sie dynamische Nonces. Der OncePerRequestFilter generiert
pro Request einen kryptographisch sicheren Nonce und setzt ihn als Request-Attribut. Thymeleaf-Templates
können den Nonce dann per ${cspNonce} referenzieren.
// src/main/java/com/example/filter/CspNonceFilter.java
package com.example.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import org.springframework.web.filter.OncePerRequestFilter;
import java.security.SecureRandom;
import java.util.Base64;
@Component
public class CspNonceFilter extends OncePerRequestFilter {
private static final SecureRandom RANDOM = new SecureRandom();
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws Exception {
// Nonce generieren (22 Zeichen Base64)
byte[] bytes = new byte[16];
RANDOM.nextBytes(bytes);
String nonce = Base64.getEncoder().encodeToString(bytes);
// Nonce als Request-Attribut fuer Thymeleaf
request.setAttribute("cspNonce", nonce);
// CSP-Header mit Nonce setzen
String csp = "default-src 'self'; "
+ "script-src 'self' 'nonce-" + nonce + "' 'strict-dynamic'; "
+ "style-src 'self' 'unsafe-inline'; "
+ "img-src 'self' data: https:; "
+ "font-src 'self'; "
+ "connect-src 'self'; "
+ "object-src 'none'; "
+ "frame-ancestors 'self'; "
+ "base-uri 'self'; "
+ "form-action 'self'";
response.setHeader("Content-Security-Policy", csp);
filterChain.doFilter(request, response);
}
} <!-- Thymeleaf-Template: Nonce verwenden -->
<script th:attr="nonce=${cspNonce}">
console.log('Autorisiertes Script mit Nonce');
</script>
<!-- Externe Scripts mit Nonce -->
<script th:attr="nonce=${cspNonce}"
src="/js/app.js"></script> Verifizierung
Prüfen Sie den CSP-Header mit curl. Der Nonce aendert sich bei jedem Request -- das ist korrekt und gewuenscht.
# CSP-Header pruefen
curl -sI http://localhost:8080 | grep -i content-security-policy
# Erwartete Ausgabe:
content-security-policy: default-src 'self'; script-src 'self' 'nonce-abc123...' 'strict-dynamic'; ... Häufige Fehler bei CSP in Spring Boot
SecurityFilterChain und OncePerRequestFilter setzen beide CSP
Wenn sowohl die contentSecurityPolicy()-DSL als auch ein OncePerRequestFilter den CSP-Header setzen, entstehen doppelte Header. Verwenden Sie für Nonces ausschliesslich den Filter und entfernen Sie die CSP-Konfiguration aus der SecurityFilterChain.
CORS-Config kollidiert mit CSP connect-src
Spring Boots @CrossOrigin oder CorsConfigurationSource erlaubt Cross-Origin-Requests auf Server-Seite, aber CSP blockiert sie im Browser. Fuegen Sie erlaubte Origins auch in die connect-src-Direktive ein.
Static Resources haben andere CSP als Controller-Responses
Statische Dateien unter /static/** werden standardmaessig mit den gleichen Headern wie Controller-Responses ausgeliefert. Wenn Ihr Nonce-Filter nur Controller-Requests verarbeitet, fehlt der Nonce auf statischen Seiten.
Profile dev hat unsafe-inline -- vergessen umzuschalten
Ein Dev-Profil mit 'unsafe-inline' in script-src ist bequem zum Entwickeln. Wenn das falsche Profil auf Produktion aktiv ist, fehlt der XSS-Schutz. Prüfen Sie das aktive Profil nach jedem Deployment mit /actuator/env.
Compliance-Anforderungen
Content Security Policy ist eine zentrale Anforderung mehrerer Compliance-Frameworks und besonders relevant für Spring-Boot-Anwendungen, die sensible Daten verarbeiten.
Wie steht Ihre Domain bei Content Security Policy?
Prüfen Sie es jetzt — kostenlos, ohne Registrierung, mit 166 Prüfpunkte.