Jul 10, 2026·7 min read·4 visits
Unsafe in-memory caching in API Platform Core's JSON:API and HAL normalizers leaks sensitive properties across different user contexts when running under persistent PHP environments like FrankenPHP or RoadRunner.
CVE-2026-49858 is a vulnerability in API Platform Core's JSON:API and HAL item normalizers where conditionally secured attributes are cached globally in memory. When deployed in long-running PHP execution environments such as FrankenPHP worker mode, Swoole, or RoadRunner, this persistent caching bypasses property-level security constraints, allowing unprivileged users to access sensitive, unauthorized fields cached during privileged requests.
API Platform Core is a highly extensible framework built on top of the Symfony ecosystem designed to build modern API-driven projects. The framework includes specialized serializers for industry-standard formats such as HAL and JSON:API. These serializers use custom normalizer classes to map internal PHP entity structures into correctly formatted payload responses. To control data exposure dynamically, developers can utilize property-level security declarations that evaluate authorization logic during the serialization process.
The dynamic evaluation of authorization rules introduces computational overhead. To optimize performance, the framework implements an in-memory caching mechanism that stores the calculated structural representation of normalized resources. The vulnerability lies within this caching optimization layer, specifically within the HAL and JSON:API item normalizers. When property-level security constraints are applied to a resource, the serializer fails to validate whether the calculated cache key is safe for multi-user contexts.
The impact of this design flaw is realized when the application is hosted on modern, persistent PHP application servers. In traditional CGI or PHP-FPM architectures, the entire in-memory state is flushed at the end of each HTTP request, neutralizing the risk of cross-user cache leakage. However, under long-running runtimes like FrankenPHP in worker mode, RoadRunner, Swoole, or ReactPHP, the memory state persists across thousands of independent HTTP requests. This persistence allows a cached resource structure generated during a privileged request to be served directly to subsequent, unauthorized users.
The root cause of the vulnerability is the unsafe reuse of the computed structure cache in the ItemNormalizer classes for HAL and JSON:API. Specifically, ApiPlatform\JsonApi\Serializer\ItemNormalizer and ApiPlatform\Hal\Serializer\ItemNormalizer maintain an internal class property called componentsCache. This cache maps the resource structural components (attributes, relationships, and links) to specific format contexts using a key derived from $context['cache_key'].
In vulnerable versions of the framework, the normalizers unconditionally generated and applied the cache key using the getCacheKey() method from CacheKeyTrait. The generation process did not evaluate whether the target resource class declared property-level security attributes, such as #[ApiProperty(security: 'is_granted("ROLE_ADMIN")')]. Because this key was treated as safe by default, any request targeting the resource would resolve to the same cache slot regardless of the requester's security scope.
When a highly privileged user, such as an administrator, requests a resource, the serializer evaluates the security expressions on each property. Since the admin is authorized, the restricted attributes are validated, and the resulting structure is stored inside the componentsCache property. When a subsequent request is processed by the same worker thread on behalf of an unprivileged user, the normalizer checks the componentsCache using the generic cache key. Because the cache key matches, the normalizer returns the cached administrative representation directly, bypassing all property-level security evaluations.
To resolve this security vulnerability, the maintainers integrated the isCacheKeySafe verification mechanism into the serialization process of both the JSON:API and HAL serializers. This mechanism analyzes the target resource class to detect any properties configured with dynamic security constraints. If any security annotation or attribute is detected, the caching mechanism is disabled for that entire resource class, preventing cross-user pollution.
// In src/Serializer/AbstractItemNormalizer.php (Base class modification)
/**
* Check if any property contains a security grant, which makes the cache key not safe,
* as allowed_properties can differ for two instances of the same object.
*/
protected function isCacheKeySafe(array $context): bool
{
if (!isset($context['resource_class']) || !$this->resourceClassResolver->isResourceClass($context['resource_class'])) {
return false;
}
$resourceClass = $this->resourceClassResolver->getResourceClass(null, $context['resource_class']);
if (isset($this->safeCacheKeysCache[$resourceClass])) {
return $this->safeCacheKeysCache[$resourceClass];
}
$options = $this->getFactoryOptions($context);
$propertyNames = $this->propertyNameCollectionFactory->create($resourceClass, $options);
$this->safeCacheKeysCache[$resourceClass] = true;
foreach ($propertyNames as $propertyName) {
$propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName, $options);
if (null !== $propertyMetadata->getSecurity()) {
// Disables caching if a single property has a dynamic security policy
$this->safeCacheKeysCache[$resourceClass] = false;
break;
}
}
return $this->safeCacheKeysCache[$resourceClass];
}The ItemNormalizer implementation for JSON:API was updated to call this gate before assigning the cache key. A similar modification was introduced to the HAL ItemNormalizer. The code below shows the comparison between the vulnerable and patched cache key assignment:
// In src/JsonApi/Serializer/ItemNormalizer.php
if (!isset($context['cache_key'])) {
- $context['cache_key'] = $this->getCacheKey($format, $context);
+ $context['cache_key'] = $this->isCacheKeySafe($context) ? $this->getCacheKey($format, $context) : false;
}// In src/Hal/Serializer/ItemNormalizer.php
if (!isset($context['cache_key'])) {
- $context['cache_key'] = $this->getCacheKey($format, $context);
+ $context['cache_key'] = $this->isCacheKeySafe($context) ? $this->getCacheKey($format, $context) : false;
}By setting $context['cache_key'] to false, the caching layer is bypassed entirely during the serialization workflow. This ensures that the dynamic security constraints are evaluated dynamically on each subsequent request. The fix is complete and robust because it shifts the default state to non-cached whenever security attributes are present on any field of the target class.
Exploiting this vulnerability relies on the behavior of persistent PHP workers. The attacker does not need to submit custom payload parameters. Instead, they exploit the synchronization state of the application's persistent runtime. This behavior is illustrated in the sequence diagram below:
To perform the attack, an unauthorized user first identifies target endpoints that utilize the HAL or JSON:API format representation. They look for endpoints that expose standard user profiles, financial information, or administrative metadata. The attacker must target systems deployed under runtimes like FrankenPHP worker mode, Swoole, or RoadRunner where worker threads are reused across requests.
The attacker then waits for or triggers an administrative request to the target resource. When the administrator's request is handled by a specific worker, the cache is populated with the complete resource model, including the protected properties. The attacker sends rapid, successive requests to the same endpoint. When one of these requests lands on the populated worker process, the system serves the cached structure, revealing the administrator-only properties to the attacker.
The security impact of CVE-2026-49858 is primarily a high-severity confidentiality breach. Although the vulnerability does not allow an attacker to write, modify, or delete database elements, it grants direct access to restricted properties. Depending on the application schema, this may result in the exposure of personally identifiable information (PII), API tokens, system credentials, or internal configuration values.
The CVSS v3.1 base score is calculated at 5.9 (Medium severity). The attack complexity is rated as High (AC:H) because successful exploitation depends on external factors. Specifically, the system must run on a persistent worker framework, and the attacker must execute their request on the same worker process that handled a privileged request before the cache expires or the process restarts.
Because the vulnerability does not affect the host operating system directly or allow binary code execution, the impact scope is Unchanged (S:U). The integrity (I) and availability (A) ratings are both None (N). However, the high confidentiality rating means that targeted attacks against administrative endpoints present a critical data leak risk.
The primary remediation strategy is upgrading the api-platform/core package to a patched release. Ensure that your composer dependency constraints resolve to one of the following safe versions: >= 4.1.29, >= 4.2.26, or >= 4.3.12. Running composer update api-platform/core within your deployment pipeline will apply the patch.
If upgrading immediately is not possible, the caching vector can be disabled by switching off the persistent execution models of your PHP web server. Configuring FrankenPHP to run in standard request mode instead of worker mode prevents process memory from persisting across HTTP requests. Similarly, using classic PHP-FPM instead of Swoole or RoadRunner completely mitigates this vulnerability.
Alternatively, developers can implement a custom context builder to globally disable the cache key generation for affected formats. By registering a custom serializer context builder, you can intercept the serialization request and force the cache_key parameter to false for HAL and JSON:API requests. This configuration forces the normalizer to execute dynamic property security checks on every individual request at the cost of slight performance overhead.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
api-platform/core API Platform | >= 4.1.0, < 4.1.29 | 4.1.29 |
api-platform/core API Platform | >= 4.2.0, < 4.2.26 | 4.2.26 |
api-platform/core API Platform | >= 4.3.0, < 4.3.12 | 4.3.12 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-524 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 | 5.9 (Medium) |
| EPSS Score | 0.00197 |
| Impact | High Confidentiality Exposure |
| Exploit Status | None (No public exploit or PoC) |
| KEV Status | Not Listed |
The product uses a cache that contains sensitive information, but it does not adequately prevent unauthorized access to that information.
A high-severity denial-of-service vulnerability in @libp2p/gossipsub prior to version 16.0.0 allows unauthenticated remote attackers to trigger event loop starvation and complete node freeze by exploiting unbounded protobuf decoding limits and nested synchronous array iteration loops.
CVE-2026-5078 is a log injection vulnerability in Morgan, the widely deployed Node.js HTTP request logging middleware. The vulnerability arises because the ':remote-user' logging token decodes and outputs basic authentication usernames containing control characters, such as Carriage Return (CR) and Line Feed (LF), without sanitization. An unauthenticated attacker can bypass native HTTP header parsers by Base64-encoding CRLF sequences in the Authorization header. When Morgan logs the request, these control characters force newlines in the log stream, enabling log forging, SIEM evasion, and system activity spoofing.
CVE-2026-48861 is a client-side HTTP request-line CRLF (Carriage Return Line Feed) injection vulnerability in the popular Elixir HTTP client library, Mint. The vulnerability permits HTTP Request Splitting and HTTP Request Smuggling when an application forwards untrusted, attacker-controlled inputs to Mint's HTTP client requests as either the HTTP request method or target. By embedding CRLF characters within these parameters, an attacker can terminate the request line prematurely, inject malicious headers, or pipeline entirely independent requests. These smuggled requests are then processed by upstream or downstream proxy servers as separate HTTP queries on the same TCP connection. While Mint version 1.7.0 introduced target validation to secure the request target, the HTTP request method parameter remained completely unvalidated. This flaw allows attackers to bypass routing filters, access restricted internal APIs, or poison HTTP caches under default configurations.
An Inconsistent Interpretation of HTTP Requests (HTTP Request/Response Smuggling) vulnerability in the Elixir Mint HTTP client allows attacker-controlled HTTP/1 servers to desynchronize response framing on shared connections due to over-lenient parsing of sign-prefixed Content-Length headers.
An allocation of resources without limits or throttling vulnerability in Elixir Mint allows an attacker-controlled HTTP/2 server to exhaust memory in a Mint client. The vulnerability is exploited by sending a HEADERS frame without the END_HEADERS flag followed by an infinite stream of CONTINUATION frames. Because the client lacks limits on the incoming header-block accumulator, the client continuously consumes memory until an out-of-memory crash occurs.
CVE-2026-48596 is an Improper Neutralization of CRLF Sequences in HTTP Headers (HTTP Request/Response Splitting, CWE-113) in the Elixir Tesla HTTP client. The flaw resides in how multipart content-type parameters are joined and serialized, enabling attackers to inject arbitrary headers or split HTTP requests when applications pass untrusted inputs to the parameters of multipart uploads.