Safeguard
DevSecOps

spring-web Maven Dependency: Known CVEs and How to Stay Patched

The spring-web Maven artifact pulls a lot of transitive weight and has been at the center of high-profile RCE bugs. Here is what to watch and how to keep it patched.

Karan Patel
Platform Engineer
6 min read

The spring-web Maven dependency is one of the most widely deployed Java libraries on the planet, and because it sits directly on the request-handling path it has been the target of some of the most serious remote code execution bugs in the Spring ecosystem. If you run org.springframework:spring-web anywhere in your dependency tree, you inherit both its capabilities and its patch obligations. This guide covers what the artifact does, the vulnerabilities that have mattered, and a workflow to keep it current.

What the spring-web artifact actually provides

spring-web is the foundational web module of the Spring Framework. It ships the core HTTP abstractions — HttpMessageConverter, the RestTemplate and WebClient plumbing, multipart handling, CORS support, and the data-binding machinery that maps request parameters onto Java objects. Almost every Spring MVC or WebFlux application pulls it in, usually transitively through a Spring Boot starter rather than as a direct declaration.

A typical direct coordinate looks like this:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>6.1.14</version>
</dependency>

In practice most teams never write that block. They declare spring-boot-starter-web, and Spring Boot's dependency management resolves a managed spring-web version for them. That indirection is convenient, but it means the version you are actually shipping is easy to lose track of — which is exactly how outdated, vulnerable copies survive in production.

The CVE that made spring-web famous: Spring4Shell

The most consequential bug tied to this artifact is CVE-2022-22965, better known as Spring4Shell. It is a critical remote code execution flaw with a CVSS score of 9.8. The root cause is in the framework's data-binding logic: on JDK 9 and later, an attacker could manipulate the class object exposed through parameter binding to reach ClassLoader properties and write an arbitrary file to disk.

The classic exploitation path targeted an application deployed as a WAR on Apache Tomcat. An attacker crafted a request that reconfigured Tomcat's access-log valve to write a web shell into a servlet-accessible directory, then hit that shell's URL to run code. The vulnerable component set spans spring-beans, spring-web, and spring-webmvc.

Affected versions were Spring Framework 5.3.0 through 5.3.17 and 5.2.0 through 5.2.19, plus older unsupported branches. The fix landed in 5.3.18 and 5.2.20. If you are on any 6.x line you are well clear of the original flaw, but the lesson holds: a single stale minor version on the request path is enough to hand over the host.

Note that Spring4Shell is distinct from the roughly contemporaneous Spring Cloud Function bug (CVE-2022-22963). They are different components, and confusing the two led a lot of teams to patch the wrong thing in April 2022.

Why transitive resolution hides your real risk

Here is the uncomfortable part. When you run a scan against your pom.xml, the version you see declared is often not the version that ends up on the classpath. Maven's nearest-wins resolution, dependency management sections in parent POMs, and Spring Boot's BOM all influence the effective version. Two modules in the same build can even resolve different spring-web versions before conflict resolution collapses them.

To see what you are really shipping, resolve the full tree rather than reading the POM:

mvn dependency:tree -Dincludes=org.springframework:spring-web

That prints every path that reaches spring-web and the version each requests. If you want the flattened, post-resolution answer, mvn dependency:list gives you the single winning version per artifact. This is the number that matters for CVE matching. An SCA tool such as Safeguard performs the same resolution automatically and flags a vulnerable transitive spring-web even when your top-level declaration looks clean.

A patching workflow that holds up

Keeping this dependency current is less about heroics and more about a repeatable loop:

  1. Pin through the Spring Boot BOM, not per-artifact. Upgrading spring-boot-starter-parent or the spring-boot-dependencies BOM moves spring-web and its siblings together, so you never end up with a mismatched spring-beans/spring-web pair.

  2. Fail the build on known-vulnerable versions. Wire a scanner into CI so a merge that reintroduces a flagged version cannot pass. The OWASP Dependency-Check Maven plugin does this locally; hosted SCA does it with a live advisory feed.

  3. Watch the actual resolved version, not the declared one. Re-run dependency:tree after any dependency change, because a seemingly unrelated upgrade can drag a new Spring transitive along.

  4. Track the framework's support timeline. Open-source support for Spring Framework 5.3 ended in 2024, meaning no free security patches. Running an EOL branch turns every future CVE into an unfunded emergency.

A quick verification you can run before a release:

# Confirm no vulnerable spring-web slipped in
mvn dependency:tree -Dincludes=org.springframework:spring-web \
  | grep spring-web

Hardening beyond the version number

Patching removes known bugs, but you can also shrink the blast radius. For the Spring4Shell class of issue, deployments running as an executable JAR with embedded Tomcat were far harder to exploit than traditional WAR-on-Tomcat setups, because the log-valve write path was not reachable the same way. Preferring the Spring Boot fat-JAR model, running with a least-privilege filesystem, and disabling unnecessary data-binding on sensitive controllers all reduce exposure. None of these replace patching — they buy you time between disclosure and rollout.

If you maintain many services, standardize the Spring Boot version across them in a shared parent POM. A fleet where every app inherits the same managed spring-web version is one upgrade away from safe; a fleet where each team pins independently is a permanent audit backlog.

FAQ

Is spring-web the same as spring-webmvc?

No. spring-web provides the core HTTP and web abstractions shared across the stack, while spring-webmvc builds the servlet-based MVC framework on top of it. Spring4Shell touched both, along with spring-beans, which is why patching only one was insufficient.

How do I find the real spring-web version my app runs?

Run mvn dependency:tree -Dincludes=org.springframework:spring-web to see every requesting path, or mvn dependency:list for the single resolved version. The resolved version, not the one declared in your POM, is what CVE matching should use.

Am I still exposed to Spring4Shell on Spring 6?

The original CVE-2022-22965 was fixed in 5.3.18 and 5.2.20, and all 6.x releases postdate the fix, so a current 6.x line is not affected by that specific flaw. Staying patched still matters because new advisories against Spring components continue to appear.

Should I declare spring-web directly in my POM?

Usually not. Let Spring Boot's dependency management resolve it through a starter and the BOM so the whole Spring stack moves in lockstep. Declaring it directly invites version drift between spring-web, spring-beans, and spring-webmvc.

Never miss an update

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