XSS (Cross-Site Scripting) in Spring Security
XSS is a critical security vulnerability where attackers inject malicious scripts into web pages viewed by other users. Here's how Spring Security helps protect against it:
Default XSS Protections in Spring Security
Spring Security provides several built-in protections:
1. X-XSS-Protection Header
Spring Security automatically adds security headers including:
X-XSS-Protection: 1; mode=block2. Content Security Policy (CSP)
You can configure CSP headers to restrict script sources:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                .contentSecurityPolicy(csp -> csp
                    .policyDirectives("script-src 'self'")
                )
            );
        return http.build();
    }
}Common XSS Vulnerabilities in Spring Boot
1. Thymeleaf Template Injection
Vulnerable Code:
@GetMapping("/greet")
public String greet(@RequestParam String name, Model model) {
    model.addAttribute("name", name); // Unescaped
    return "greeting";
}Safe Approach:
<!-- Thymeleaf automatically escapes by default -->
<p th:text="${name}">Name</p>
<!-- For unescaped HTML (use cautiously) -->
<p th:utext="${trustedHtml}">HTML</p>2. JSON Response XSS
Vulnerable:
@GetMapping("/api/user")
@ResponseBody
public String getUser(@RequestParam String name) {
    return "{\"name\": \"" + name + "\"}"; // Direct concatenation
}Safe:
@GetMapping("/api/user")
@ResponseBody
public User getUser(@RequestParam String name) {
    return new User(name); // Jackson automatically encodes
}Best Practices for XSS Prevention
1. Input Validation
import jakarta.validation.constraints.Pattern;
public class UserDTO {
    @Pattern(regexp = "^[a-zA-Z0-9\\s]+$", 
             message = "Name contains invalid characters")
    private String name;
}2. Output Encoding
import org.springframework.web.util.HtmlUtils;
@Service
public class UserService {
    public String sanitizeInput(String input) {
        return HtmlUtils.htmlEscape(input);
    }
}3. CSRF Protection (related to XSS)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            );
        return http.build();
    }
}4. Custom XSS Filter
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
public class XSSUtils {
    public static String sanitize(String input) {
        if (input == null) return null;
        return Jsoup.clean(input, Safelist.basic());
    }
}
// Usage in Controller
@PostMapping("/comment")
public String addComment(@RequestParam String comment) {
    String sanitized = XSSUtils.sanitize(comment);
    // Save sanitized comment
    return "success";
}5. Security Headers Configuration
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .headers(headers -> headers
            .xssProtection(xss -> xss.headerValue(
                XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK
            ))
            .contentSecurityPolicy(csp -> csp
                .policyDirectives("default-src 'self'; " +
                                "script-src 'self' 'unsafe-inline'; " +
                                "style-src 'self' 'unsafe-inline';")
            )
        );
    return http.build();
}Testing for XSS
@Test
public void testXSSProtection() throws Exception {
    String maliciousInput = "<script>alert('XSS')</script>";
    
    mockMvc.perform(get("/greet")
            .param("name", maliciousInput))
            .andExpect(status().isOk())
            .andExpect(content().string(
                not(containsString("<script>"))
            ));
}Key Takeaways
- Never trust user input — Always validate and sanitize
- Use template engines properly — Thymeleaf escapes by default
- Enable security headers — Spring Security provides these out-of-the-box
- Use CSP headers — Restrict where scripts can be loaded from
- Encode output — Use HtmlUtilsor Jsoup for sanitization
- Keep dependencies updated — Security patches are regularly released