CVE-2026-47850
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring Data REST turns Spring Data repositories into hypermedia-driven REST resources, exporting each aggregate root as a collection and item resource with HTTP semantics for GET, POST, PUT, PATCH and DELETE, and mapping JSON request bodies onto the persisted domain objects without any hand-written controller code.
An Authorization Bypass vulnerability (CVE-2026-47850) has been identified in the PUT payload merging logic of Spring Data REST, which allows an authenticated client with PUT access to persist its own version value for an immutable aggregate, defeating the optimistic-locking protection the server is supposed to control.
Per OWASP: Access control enforces policy such that users cannot act outside of their intended permissions. Failures typically lead to unauthorized information disclosure, modification, or destruction of all data or performing a business function outside the user's limits.
This issue affects the PUT payload merging of Spring Data REST, in applications that export a repository whose aggregate root is an immutable type, such as a Java record, a Kotlin data class or a type instantiated through an all-args persistence creator, or that permits a body-driven polymorphic subtype change, and that declares a version property visible to Jackson's deserialization model.
Details
Module Info
- Product: NES for Spring Data REST
- Affected packages:
org.springframework.data:spring-data-rest-webmvc - Affected versions: >=5.1.0 <=5.1.0, >=5.0.0 <=5.0.6, >=4.5.0 <=4.5.13, >=4.0.0 <=4.4.15, >=3.1.0 <=3.7.20
- GitHub repository: https://github.com/spring-projects/spring-data-rest
- Published packages: https://central.sonatype.com/artifact/org.springframework.data/spring-data-rest-webmvc
- Package manager: Maven
- Fixed in:
- NES for Spring Data REST lines 4.5.x, 4.4.x, 4.3.x, 4.2.x, 3.7.x, and 3.5.x, built on the supported releases 4.5.15, 4.4.18, 4.3.18, 4.2.20, 3.7.28, and 3.5.17
- OSS Spring Data REST 5.1.1 and 5.0.7
Vulnerability Info
This Medium-severity vulnerability is found in the org.springframework.data:spring-data-rest-webmvc package in the JSON payload merging layer of Spring Data REST.
Item resource PUT requests are handled by DomainObjectReader.readPut, which deserializes the client-supplied request body into a fresh object and then merges it onto the persisted instance:
// spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java
public <T> T readPut(final ObjectNode source, T target, final ObjectMapper mapper) throws Exception {
Assert.notNull(source, "ObjectNode must not be null");
Assert.notNull(target, "Existing object instance must not be null");
Assert.notNull(mapper, "ObjectMapper must not be null");
Object intermediate = mapper.readerFor(target.getClass()).readValue(source);
return (T) mergeForPut(intermediate, target, mapper);
}
Nothing in this path restores the persisted identifier and version onto the incoming payload, so the object built from the request body carries whatever values the client sent. Whether those values survive is decided entirely by mergeForPut:
// spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java
<T> T mergeForPut(T source, T target, final ObjectMapper mapper) {
Assert.notNull(mapper, "ObjectMapper must not be null");
if (target == null || source == null) {
return source;
}
boolean isTypeChange = !source.getClass().isInstance(target);
boolean immutableTarget = entities.getPersistentEntity(target.getClass())
.map(PersistentEntity::isImmutable)
.orElse(true); // Not a Spring Data managed type -> no detailed merging
return entities.getPersistentEntity(isTypeChange ? source.getClass() : target.getClass()) //
.map(it -> {
MappedProperties properties = MappedProperties.forDeserialization(it, mapper);
if (isTypeChange || immutableTarget || it.isImmutable()) {
copyRemainingProperties(properties.getIgnoredProperties(), target, source);
return source;
}
MergingPropertyHandler propertyHandler = new MergingPropertyHandler(source, target, it, mapper);
it.doWithProperties(propertyHandler);
it.doWithAssociations(new LinkedAssociationSkippingAssociationHandler(associationLinks, propertyHandler));
return target;
}).orElse(source);
}
For a mutable aggregate the merge walks the persisted target property by property, and the handler that applies each value refuses to touch server-owned state:
// spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java (MergingPropertyHandler)
public void doWithPersistentProperty(PersistentProperty<?> property) {
if (property.isIdProperty() || property.isVersionProperty() || !property.isWritable()) {
return;
}
...
}
For an immutable target, or when the request body selects a different polymorphic subtype, the method takes the early branch instead: it returns source, the object constructed from the request body, and copies back only the properties Jackson was told to ignore. The identifier and version guard above is never reached, because no merge onto the persisted instance happens at all. The identifier is re-applied later by the request-handling layer, but the version property is not, so a version supplied in the PUT body is written straight through to the store.
Because the version property is what Spring Data uses for optimistic locking, a client can send a stale or arbitrary version with an otherwise ordinary PUT and have it accepted. That silently discards the lost-update protection the aggregate declares, letting a stale write overwrite a concurrent update on applications that rely on the version property rather than mandatory If-Match and ETag preconditions. Only network access and an account permitted to issue PUT requests against the exported resource are required.
The last public release of the 4.5 line, 4.5.13, still lacks the identifier and version retention step, so that line is affected through its latest publicly available build.
Mitigation
Only recent versions of Spring Data REST receive community support. Older lines are End-of-Life and will not receive public updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Spring Data REST.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- The published advisory for this issue does not credit an external finder.