Laravel’s built-in TokenGuard can be fooled into signing in a request as another user. The advisory, GHSA-f9x3-7vq9-cph6 (CVE-2026-114509), calls it critical and gives it a CVSS 4.0 score of 9.2. It applies when an app uses the standard token-based API guard and keeps its users in MySQL or MariaDB under certain configurations. The fixed versions of laravel/framework are 12.68.0 and 13.29.0.
The dates are worth reading closely. The code change and both fixed releases are dated 25 August 2026. Laravel published the advisory on GitHub on 19 October 2026, and NVD added its record on 21 October. This post follows one request through the guard, shows the lines that changed and ends with the checks and the order of work. The facts also sit on one page, our CVE-2026-114509 vulnerability entry.
- RequestThe client sends api_token as an array instead of a string.
- TokenGuardThe user() method only tests that the value is not empty.
- User providerAn array value becomes a whereIn lookup on the token column.
- MySQL or MariaDBThe column is compared with a number and a user record can match.
How Does One Request Log In as Someone Else?
In Laravel, a guard decides how each request is authenticated. TokenGuard reads an API token from the request and finds the user who owns it. In the source at tag v12.67.0, the last 12.x release before the fix, the guard looks in four places for the token. These are the query string, the request input, the bearer token and the HTTP basic password.
The guard’s user() method then asked one question: is the token not empty? PHP’s manual lists an array with zero elements among the values that count as false, so any array with an item counts as true. An array with a single item therefore passed the check, although a token should be a string. The test that came with the fix uses exactly that input, a request whose api_token is the array [0].
With the guard’s hash option left at its default of false, nothing else stood between that value and the database. With the Eloquent user provider, the retrieveByCredentials method checks each credential value. When the value is an array, it builds a whereIn query instead of a plain where. The lookup stops asking which user has this exact token and asks which user has a token from this list.
Why Does the Database Pick a Real User?
In the fix’s test the list holds the number 0, while the token column holds text. MySQL’s manual says that when a string is compared with a number, the comparison is made on floating-point numbers. The advisory says that under certain MySQL and MariaDB configurations the query can match “an unintended user record” instead of failing. NVD repeats this. The manual’s own example shows SELECT 0 = 'x6' returning 1. Neither names the configurations, so we stop at the advisory’s wording.
What Did the Two-Line Fix Change?
Two commits, both titled “fix token retrieval”, are dated 25 August 2026, at 01:15 and 01:16 UTC. NVD cites both. They change src/Illuminate/Auth/TokenGuard.php in two places. In user(), the check now also requires a string. In validate(), a credential that is not a string returns false.
- if (! empty($token)) {
+ if (is_string($token) && ! empty($token)) {
The same commits add two tests. Each sends the array [0] and expects the guard to refuse it without ever calling the user provider.
The tagged commits followed the same day: v12.68.0 at 14:18 UTC and v13.29.0 at 20:57 UTC. The new check is already in the file at tags v13.27.0 and v13.28.0, both from the same day. The advisory names 13.29.0 as the fixed version, so use that number. GitHub’s release page for 12.68.0 lists one item, “Fix resource loading”. The page for 13.29.0 has no text. Neither mentions authentication.
Which Apps Run This Guard?
The advisory says the flaw applies “when configured with a standard token-based API guard”. Its version ranges are laravel/framework below 12.68.0 and below 13.29.0. The CVE record lists below 12.68.0, and 13.0.0 up to below 13.29.0.
That points to config/auth.php. In the laravel/laravel skeleton, tag v8.5.0 (8 December 2020) defined an api guard with 'driver' => 'token' and 'hash' => false. From tag v8.6.0 (17 August 2021) on, the file lists only the web session guard. An app started from the older skeleton may still carry the token guard, and so may any app whose team added a token driver later.
Laravel’s own documentation separates the other API options. It says Sanctum uses a sanctum guard and hashes API tokens with SHA-256 before storing them. It says Passport needs a guard whose driver is passport, and it speaks of “Passport’s TokenGuard”. The advisory names the framework’s built-in TokenGuard and does not mention Sanctum or Passport. Check your own guard names instead of assuming.
Older lines need care. Laravel’s support policy page says security fixes for Laravel 11 ended on 12 March 2026 and for Laravel 10 on 4 February 2025. Laravel 12 gets them until 24 February 2027 and Laravel 13 until 17 March 2028. The advisory names fixed versions for 12.x and 13.x only. Its first range, below 13.29.0, has no lower bound, and it names no fixed 10.x or 11.x release.
Who Has Published Exploit Code?
The flaw is not in CISA’s Known Exploited Vulnerabilities (KEV) catalog as of the catalog’s 23 October 2026 update. A proof-of-concept exploit is a different matter.
Three public GitHub repositories about this flaw turned up in our search on 24 October. The first was created on 20 October 2026 at 01:52 UTC, about 12 hours after the advisory. Its README says it holds a lab with a vulnerable and a patched app plus a script. The script gets in as an existing user on the vulnerable build and is refused on the patched one.
Two more appeared: a local reproduction on 20 October and an educational write-up on 22 October. We read them, did not run them and do not link them.
Security teams said so on X too. Hakai Security, a security company, posted on 20 October:
“New Laravel one-day exploit by Yokai Research!” @HakaiOffsec, 20 October. The company says its team reproduced the bug after the advisory went public and published a working proof of concept and exploit, with a write-up.
Two days later Italy’s national CSIRT said a proof of concept is available, and the national cybersecurity agency ACN published the matching alert on its site.
“disponibile Proof of Concept (PoC) per lo sfruttamento della CVE-2026-114509” (Italian: a proof of concept for exploiting the CVE is available), @csirt_it, 22 October. The ACN alert says the flaw can let an attacker impersonate another user, lists Laravel versions before 13.29.0 and before 12.68.0, and tells readers to update.
How Do You Find the Guard in Your Own App?
Follow the request path backwards. Each step answers one question.
- Run
composer show laravel/frameworkin the project and compare the version with 12.68.0 and 13.29.0. - Open
config/auth.php. Underguards, look for an entry whose driver istoken, and note its name and itshashvalue. - Run
php artisan route:list -v. Laravel’s routing page says the-voption shows route middleware. Find routes that useauth:followed by the guard name you noted, such asauth:api. - Find out which database holds the users table. The advisory limits the flaw to certain MySQL and MariaDB setups.
- Search application or proxy logs for requests where
api_tokenarrived as a list. Standard access logs do not record request bodies, so a JSON body may only show up in those other logs.
If a log line shows an account used in a way its owner did not, treat it as account takeover. MITRE describes Valid Accounts (T1078) as abusing the credentials of existing accounts. This flaw reaches a similar result without the credential, so T1078 is the closest label for your notes, not an exact match.
What Do You Change, and in Which Order?
- Update laravel/framework to 12.68.0 or later on 12.x, or to 13.29.0 or later on 13.x. The 12.68.0 update also fixes CVE-2026-114508, another advisory published 11 seconds later. Then run
composer show laravel/frameworkagain to confirm. GitHub lists v13.38.0 (20 October) and v12.70.1 (21 October) as the newest releases. - On 11.x or 10.x, plan the move to a supported line. Laravel’s policy page lists no security fixes for them any more, and the advisory names no fixed release for them.
- Choose a look-back window for your logs. The commits are dated 25 August and the advisory 19 October. Search the window you pick for arrays in
api_token, and review sign-ins on accounts that serve API routes. - If no route uses the token guard, remove it from
config/auth.phpso nothing is left to bypass.
PHP stacks had a busy month. Our posts on PHP2Shell and on the PHP session upload progress flaw cover separate PHP issues, not this one. For this one, start with the first command in the list above. It takes a minute and tells you whether the rest of the list applies.
