Safeguard
Vulnerability Management

CVE-2022-31692: how a forward dispatch bypassed Spring Security authorization

A CVSS 9.8 flaw let a single internal forward skip Spring Security's URL-based access checks entirely — here's the root cause and the exact config fix.

Safeguard Research Team
Research
6 min read

On October 31, 2022, the NVD published CVE-2022-31692, a CVSS 9.8 critical flaw in Spring Security affecting versions 5.6.0 through 5.6.8 and 5.7.0 through 5.7.4. The bug lived in AuthorizationFilter, the servlet filter responsible for enforcing URL-pattern access rules on every incoming request, and it let an attacker reach a higher-privilege endpoint by triggering an internal FORWARD or INCLUDE dispatch that re-entered the filter chain under a different effective path than the one Spring Security had originally authorized. VMware's Tanzu Security Advisory shipped fixed versions 5.6.9 and 5.7.5 the same day, and NIST originally assigned CWE-863 at publication, revised that to NVD-CWE-noinfo in August 2023 pending further review, and CISA-ADP subsequently added a CWE-639 (Authorization Bypass Through User-Controlled Key) classification. NetApp subsequently issued its own advisory (ntap-20221215-0010) after confirming the flaw shipped downstream in Active IQ Unified Manager for both Windows and VMware vSphere. What makes this CVE worth studying isn't just its severity score — it's that the vulnerable pattern (authorization logic that trusts the original request path while ignoring how Java's Servlet API can silently re-dispatch a request) shows up in any framework that separates routing from access control. This post walks through the mechanism, why it evaded the obvious fix, and the exact configuration change that closes it.

What actually made this exploitable?

The exploitability hinges on a mismatch between how Servlet containers route requests and how AuthorizationFilter evaluated them. Spring Security's URL-pattern rules — the .antMatchers() or .authorizeHttpRequests() chains configured via HttpSecurity — are matched against the request path visible to the filter at the moment it runs. Under normal operation, that's the external path a client requested, and the filter runs once per external request. But the Servlet API also lets application code perform an internal RequestDispatcher.forward() or .include() — for example, a controller forwarding /public/upload internally to /admin/process. If AuthorizationFilter wasn't explicitly configured to re-evaluate authorization on DispatcherType.FORWARD or DispatcherType.INCLUDE requests, that second, internally-dispatched request could reach the higher-privilege endpoint's logic without ever passing through the access check tied to its real path. The CVSS 9.8 vector (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) reflects that this required no privileges, no user interaction, and no special network position — just an application that used forwards or includes internally in the way the Spring MVC ecosystem does routinely.

Why didn't upgrading dependencies alone fully close the gap?

Because the fix VMware shipped in 5.6.9 and 5.7.5 changed default behavior, but plenty of deployments had already layered custom dispatcherTypeMatchers or legacy WebSecurityConfigurerAdapter rules on top that the patched defaults didn't retroactively correct. The advisory's guidance wasn't "just bump the version" in isolation — it was version plus explicit configuration review, because Spring Security's own migration notes for authorizeHttpRequests (the successor to the older authorizeRequests DSL) call out that dispatcher-type coverage has to be intentional, not assumed. Teams that had migrated to the newer authorizeHttpRequests API but never added forward/include coverage remained exposed to the same class of bug even on a patched library version, because the vulnerability class is a configuration gap as much as a library defect. This is a recurring theme in authorization CVEs generally: the library ships a safer default, but an application's own DSL chain can still silently opt back into the unsafe behavior.

How does this compare to other path-based authorization bypasses?

CVE-2022-31692 belongs to the same broader family of path-based authorization bypasses as an earlier Spring Security issue, CVE-2016-9879 (CWE-417), where inconsistent servlet-container handling of URL path parameters in getPathInfo() let an attacker append an encoded / and slip past a security constraint on non-Tomcat containers. The mechanisms differ — one is a dispatcher-type gap, the other a path-parameter parsing inconsistency — but both show this isn't a one-off engineering slip; it's a structural risk in any framework that decouples "what path did the security layer see" from "what code actually ran." Outside the Java ecosystem, the same root cause — access control anchored to a request's initial routing metadata rather than the resource actually reached — recurs in reverse-proxy path-normalization bugs and in API gateways that authorize on a raw URL before an internal rewrite. The common defensive lesson across all of these: authorization checks must be re-evaluated at every dispatch boundary, not assumed to persist from the original request.

What's the concrete configuration fix?

First, upgrade to Spring Security 5.6.9, 5.7.5, or later (the fix is also present in all subsequent 5.8.x and 6.x releases, which superseded the 5.x line). Second, and just as important per VMware's advisory, explicitly configure which dispatcher types your authorizeHttpRequests chain protects. In practice that means using .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.INCLUDE).authenticated() — or .denyAll() for internal-only paths — rather than leaving forward and include dispatches unmatched and implicitly permitted. Teams still on the legacy authorizeRequests() DSL should treat the patch as a forcing function to migrate to authorizeHttpRequests(), since Spring's own documentation notes the newer API's matcher evaluation is stricter and easier to audit for exactly this gap. Finally, avoid designing internal routing that relies on forwarding into an endpoint that expects to only ever be reached externally and pre-authorized — treat every dispatch type as a fresh authorization boundary.

How should teams catch this class of bug going forward?

Software composition analysis is the first line of defense here: any SCA tool checking Maven or Gradle dependency trees against the NVD will flag org.springframework.security:spring-security-web versions in the 5.6.0–5.6.8 and 5.7.0–5.7.4 ranges as vulnerable to CVE-2022-31692 the moment it's declared, direct or transitive. But version-matching alone tells you the library is present, not whether your specific authorizeHttpRequests configuration actually re-checks forward and include dispatches — that requires reading the security configuration itself, which is where source-level static analysis of the authorization DSL complements a dependency scan. Safeguard's SCA engine flags the vulnerable Spring Security version range directly against NVD and GHSA advisory data as part of its Java/Maven and Gradle dependency coverage, and its SAST source-to-sink dataflow analysis is built to trace authorization-relevant logic through a codebase — giving teams a starting point for auditing whether their own filter chain configuration, not just their dependency version, closes this specific gap.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.