Spring Security Lazy Header Writing Security Header Bypass (CVE-2026-22732)
by Drew Dennison / semgrep (GitHub: semgrep) · 2026-07-05
- Severity
- Critical
- CVE
- CVE-2026-22732
- Category
- web
- Affected product
- Spring Security (Spring Boot applications)
- Affected versions
- 5.7.0–5.7.21, 5.8.0–5.8.23, 6.3.0–6.3.14, 6.4.0–6.4.14, 6.5.0–6.5.8, 7.0.0–7.0.3
- Disclosed
- 2026-07-05
- Patch status
- unpatched
Tags
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-04 |
| Author / Researcher | Drew Dennison / semgrep (GitHub: semgrep) |
| CVE / Advisory | CVE-2026-22732 |
| Category | web |
| Severity | Critical |
| CVSS Score | 9.1 (CVSSv3.1, per source README) |
| Status | PoC |
| Tags | spring-security, spring-boot, security-headers, response-splitting, x-frame-options, hsts, java, junit |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Spring Security (Spring Boot applications) |
| Versions Affected | 5.7.0–5.7.21, 5.8.0–5.8.23, 6.3.0–6.3.14, 6.4.0–6.4.14, 6.5.0–6.5.8, 7.0.0–7.0.3 |
| Language / Platform | Java (Spring Boot / Spring Security) |
| Authentication Required | No |
| Network Access Required | Yes |
Summary
CVE-2026-22732 affects Spring Security’s default “lazy” header-writing mechanism, which normally injects security-related response headers (X-Frame-Options, X-Content-Type-Options, Cache-Control, Strict-Transport-Security, etc.) just before the HTTP response is committed. If controller code writes directly to response.getOutputStream(), calls response.flushBuffer(), or sets Content-Length via setIntHeader(), the response is committed before Spring Security’s header writer runs, so all of these protective headers are silently dropped. The included demo Spring Boot application defines one safe endpoint (/safe) and three vulnerable endpoints (/vuln/stream, /vuln/flush, /vuln/content-length) that each trigger the early-commit condition, plus a JUnit test suite that fails on vulnerable versions (headers missing) and passes once Spring Security is upgraded to a patched release. No authentication or user interaction is required to observe or exploit the missing-header condition on any endpoint that streams data or sets response headers early — this is a common pattern for file downloads, streaming APIs, and manual JSON responses.
Vulnerability Details
Root Cause
Spring Security’s HeaderWriterFilter writes security headers lazily, immediately before the response commits; certain servlet API calls made directly by application code (getOutputStream() writes, flushBuffer(), setIntHeader("Content-Length", ...)) commit the response earlier than expected, causing the header writer to be skipped entirely for that response.
Attack Vector
- Identify a Spring Boot endpoint backed by vulnerable Spring Security versions that streams a file, flushes the response manually, or sets
Content-Lengthdirectly (common in download/streaming/manual-JSON endpoints). - Send a normal HTTP request to that endpoint.
- Observe that expected security headers (
X-Frame-Options,X-Content-Type-Options,Cache-Control, HSTS, etc.) are absent from the response, even thoughSecurityConfigexplicitly configures them. - Chain the missing headers with a separate client-side vector appropriate to the dropped protection — e.g., clickjacking via missing
X-Frame-Options, MIME-sniffing attacks via missingX-Content-Type-Options, or downgrade/cookie theft via missing HSTS.
Impact
Silent removal of critical defense-in-depth response headers on affected endpoints, increasing exposure to clickjacking, MIME-sniffing, caching of sensitive responses, and transport-downgrade attacks, without any indication in the response that protections are missing.
Environment / Lab Setup
Target: Spring Boot application using an affected Spring Security version
Attacker: curl / any HTTP client; Java 17+ and Maven (via ./mvnw) to run the demo locally
Proof of Concept
PoC Script
See
src/main/java/com/example/vuln/VulnController.java,SecurityConfig.java, and the test insrc/test/java/com/example/vuln/HeaderVerificationTest.javain this folder.
| |
Running the app and diffing curl -i output between /safe and the three /vuln/* endpoints shows the configured security headers present on /safe but absent on all three vulnerable endpoints; ./mvnw test runs HeaderVerificationTest, which fails on vulnerable Spring Security versions and passes once upgraded to 6.5.9+ / 7.0.4+.
Detection & Indicators of Compromise
Signs of compromise:
- Automated header-scanning (e.g., securityheaders.com-style checks) reporting inconsistent header presence across endpoints of the same Spring Boot application
- Application code review revealing direct
response.getOutputStream(),flushBuffer(), orsetIntHeader("Content-Length", ...)calls in controller methods - Reports of clickjacking or MIME-sniffing issues limited to specific streaming/download endpoints while other endpoints are unaffected
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade Spring Security to 6.5.9+ or 7.0.4+ (or the corresponding patched release for your branch) |
| Interim mitigation | Force eager header writing by setting HeaderWriterFilter.setShouldWriteHeadersEagerly(true) via an ObjectPostProcessor, as shown (commented out) in SecurityConfig.java |
References
Notes
Mirrored from https://github.com/semgrep/cve-2026-22732-demo on 2026-07-05.