CVE-2026-82416 Overview
FrankenPHP is a modern application server for PHP built on Caddy. CVE-2026-82416 is a race condition vulnerability [CWE-362] affecting FrankenPHP worker mode when the PHP session extension is loaded as a shared module rather than statically linked. In this build configuration, the HAVE_PHP_SESSION macro is undefined at compile time, so frankenphp_reset_session_state() is compiled out. The session identifier PS(id) can survive a request boundary, causing cross-user session exposure under concurrent load. The issue is fixed in version 1.12.5.
Critical Impact
Under concurrent requests, session_id() can return another client's identifier or a stale identifier from a previous request on the same worker thread, enabling cross-user session exposure and session hijacking.
Affected Products
- FrankenPHP versions prior to 1.12.5 built with the PHP session extension as a shared module
- Custom FrankenPHP builds where HAVE_PHP_SESSION is unavailable at compile time
- Not affected: official static binaries and Docker images (session extension is statically linked)
Discovery Timeline
- 2026-10-13 - CVE-2026-82416 published to NVD
- 2026-10-13 - Last updated in NVD database
Technical Details for CVE-2026-82416
Vulnerability Analysis
FrankenPHP worker mode keeps the PHP interpreter resident across HTTP requests to avoid per-request bootstrap costs. Between requests, the server must reset request-scoped PHP state, including the session module globals. The reset path in frankenphp_reset_session_state() is guarded by the HAVE_PHP_SESSION compile-time macro.
When the session extension is linked as a shared module and loaded at runtime, HAVE_PHP_SESSION is not defined. The reset function is compiled out entirely. The PS(id) global that holds the active session identifier persists across request boundaries on the same worker thread.
On a subsequent request, session_start() observes that an identifier is already present and skips reading the new request's session cookie. The thread serves the request using the previous client's session, producing cross-user data exposure, session hijacking opportunities, and intermittent logout behavior for legitimate users.
Root Cause
The root cause is a conditional-compilation gap. MODULES_TO_RELOAD originally listed only filter, assuming the session cleanup path handled by frankenphp_reset_session_state() would always be available. For shared-module builds that assumption fails, leaving session state uncleaned between requests on the same worker thread.
Attack Vector
An attacker does not need authentication or user interaction. Any concurrent request stream against a vulnerable worker can trigger the race. One client's request completes, leaves PS(id) populated, and a second client's request arrives on the same worker thread before any reset occurs. The second client's session_start() adopts the stale identifier.
*
* @see https://github.com/DataDog/dd-trace-php/pull/3169 for an example
*/
-static const char *MODULES_TO_RELOAD[] = {"filter", NULL};
+static const char *MODULES_TO_RELOAD[] = {
+ "filter",
+#ifndef HAVE_PHP_SESSION
+ /* When the session extension is not visible at build time (e.g. it is
+ * loaded as a shared module), frankenphp_reset_session_state() is
+ * compiled out. Reset the session module through the registry instead so
+ * PS(id) is cleared between worker requests and a stale id cannot bleed
+ * across concurrent clients. See GHSA-v3ph-cgqh-r8p5.
+ *
+ * Unlike frankenphp_reset_session_state(), the module RSHUTDOWN frees
+ * save handlers registered from the worker bootstrap; that is an
+ * acceptable trade-off for a build where the extension isn't linked. */
+ "session",
+#endif
+ NULL};
frankenphp_version frankenphp_get_version() {
return (frankenphp_version){
Source: GitHub Commit be24d82. The patch adds the session module to the per-request reload list when HAVE_PHP_SESSION is undefined, forcing an RSHUTDOWN that clears PS(id) between worker requests.
Detect Identity Attacks the Moment Authentication Fails
Singularity™ Identity surfaces credential misuse, lateral movement, and privilege escalation in real time, so you can contain the breach before it spreads.
Stop Identity AttacksDetection Methods for CVE-2026-82416
Indicators of Compromise
- Users reporting unexpected logout events or appearing authenticated as another user
- Application logs showing session identifiers that do not match the PHPSESSID cookie sent in the request
- Audit records showing actions attributed to a session owner whose source IP or user agent changes mid-session
- FrankenPHP builds where php -m lists session and the binary was not produced from the official static distribution
Detection Strategies
- Inspect the FrankenPHP build configuration to confirm whether the session extension is statically linked or loaded as a shared module
- Add server-side logging that records the session identifier alongside the client-supplied cookie value for each request and flag mismatches
- Correlate authenticated user IDs with source IP and user-agent per session and alert on sudden changes within a single session lifetime
Monitoring Recommendations
- Monitor the FrankenPHP process for unexpected version strings; confirm the running build is 1.12.5 or later
- Track rate of session_regenerate_id() failures and anomalous session_start() behavior in PHP error logs
- Alert on application-layer events where the authenticated principal changes without an intervening logout or login event
How to Mitigate CVE-2026-82416
Immediate Actions Required
- Upgrade FrankenPHP to version 1.12.5 or later
- Switch to the official FrankenPHP static binaries or Docker images, which link the session extension statically and are not vulnerable
- If an upgrade is not immediately possible, disable worker mode and run FrankenPHP in classic request-per-process mode
- Rotate active session identifiers after patching to invalidate any session that may have been exposed
Patch Information
The fix is published in FrankenPHP release caddy/v1.12.5 and tracked in GHSA-v3ph-cgqh-r8p5. The code change is in pull request #2523 and commit be24d82, which adds the session module to MODULES_TO_RELOAD when HAVE_PHP_SESSION is undefined.
Workarounds
- Rebuild FrankenPHP with the PHP session extension linked statically so HAVE_PHP_SESSION is defined at compile time
- Disable worker mode in the Caddyfile until the upgrade is applied
- Reduce worker concurrency to a single thread per worker to shrink the race window, understanding this is a partial mitigation only
# Caddyfile: disable worker mode as a temporary workaround
{
frankenphp {
# Comment out or remove any worker directives below
# worker /path/to/app/public/index.php 4
}
}
# Verify the running FrankenPHP version after upgrade
frankenphp version
# Confirm the session extension linkage
frankenphp php-cli -r 'echo extension_loaded("session") ? "loaded\n" : "missing\n";'
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

