CVE
CVE-2026-114509, CVE-2026-114508
CWE
CWE-287, CWE-1287, CWE-470, CWE-863
Affected Surface
- Composer package `laravel/framework` versions `< 12.68.0` and `>= 13.0.0, < 13.29.0` for `CVE-2026-114509` (`TokenGuard` non-string credential authentication bypass on MySQL/MariaDB)
- Composer package `laravel/framework` versions `>= 12.45.0, < 12.68.0` and `>= 13.0.0, < 13.27.0` for `CVE-2026-114508` (`JsonApiResource` nested `include` relationship resolution)
- Composer package `livewire/livewire` versions `>= 4.3.0, < 4.4.8` for `GHSA-mhf7-xp6r-6qrc` (`#[Authorize]` attribute on component actions, `#[On]` event listeners, and `#[Json]` methods)
TL;DR
On 23 October 2026, the GitHub Advisory Database published three reviewed advisories for Laravel (laravel/framework) and Livewire (livewire/livewire):
CVE-2026-114509(GHSA-f9x3-7vq9-cph6, CVSS v4.0 9.2 Critical) inlaravel/framework(< 12.68.0and>= 13.0.0, < 13.29.0):Illuminate\Auth\TokenGuardchecked! empty($token)without verifyingis_string($token). When an attacker submits a non-string credential such as?api_token[]=0or{"api_token": [0]}against a standard unhashedtokenAPI guard backed by MySQL or MariaDB,EloquentUserProvider::retrieveByCredentials()binds0intoWHERE api_token = ?. Under MySQL and MariaDB’s implicit string-to-integer comparison rules, any non-numeric token string (such as a random hex or Base64 token starting with a letter) coerces to0and matches on the first row, logging the unauthenticated caller in without a valid token.CVE-2026-114508(GHSA-5wcm-3v2w-8362, Medium) inlaravel/framework(>= 12.45.0, < 12.68.0and>= 13.0.0, < 13.27.0): InJsonApiResource(ResolvesJsonApiElements),compileResourceRelationships()passed nested?include=segments directly into$relatedModels->loadMissing($relations)before checking whether the nested segment was declared in the related resource’stoRelationships(). RequestingGET /users/1?include=comments.truncateon a read-only endpoint caused Eloquent to invoketruncate()on theCommentquery builder, emptying thecommentstable.GHSA-mhf7-xp6r-6qrc(Medium) inlivewire/livewire(>= 4.3.0, < 4.4.8, fixed in4.4.8): Livewire 4’s#[Authorize]attribute resolved model arguments by array key ($params[0]) while PHP spread$paramspositionally (...$params) when invoking the component action or#[On]listener. Sending"params": {"1": 2, "0": 1}authorized model1(owned by the caller) and passed model2(owned by another user) to the method. Separately, methods carrying both#[Json]and#[Authorize]executed their method body before the authorization check ran.
Upgrade laravel/framework to 12.68.0 or 13.29.0 (or later) and livewire/livewire to 4.4.8 (or later).
Affected packages and projects
| Ecosystem | Package | CVE / GHSA | Severity | Affected versions | Fixed version | Vulnerable component |
|---|---|---|---|---|---|---|
| Packagist | laravel/framework | CVE-2026-114509 (GHSA-f9x3-7vq9-cph6) | CVSS v4.0 9.2 Critical | < 12.68.0, >= 13.0.0, < 13.29.0 | 12.68.0, 13.29.0 | src/Illuminate/Auth/TokenGuard.php (user() and validate()) |
| Packagist | laravel/framework | CVE-2026-114508 (GHSA-5wcm-3v2w-8362) | CVSS v3.1 6.5 Medium | >= 12.45.0, < 12.68.0, >= 13.0.0, < 13.27.0 | 12.68.0, 13.27.0 | src/Illuminate/Http/Resources/JsonApi/Concerns/ResolvesJsonApiElements.php |
| Packagist | livewire/livewire | GHSA-mhf7-xp6r-6qrc | CVSS v3.1 6.5 Medium | >= 4.3.0, < 4.4.8 | 4.4.8 | src/Mechanisms/HandleComponents/HandleComponents.php, SupportEvents.php, SupportJson.php |
Technical analysis
1. CVE-2026-114509: TokenGuard non-string credential bypass on MySQL and MariaDB
When a Laravel application configures an API guard using the built-in token driver in config/auth.php ('driver' => 'token', with the default 'hash' => false), TokenGuard::getTokenForRequest() reads api_token from the query string ($request->query('api_token')), the request body ($request->input('api_token')), the Bearer token, or the HTTP Basic password.
Because $request->query() and $request->input() can return an array when the client sends GET /api/user?api_token[]=0 or POST JSON {"api_token": [0]}, $token is not guaranteed to be a string. Before commit 8dbb08bcca (12.x) and commit 9b21ce0a9b (13.x), TokenGuard::user() and TokenGuard::validate() only checked ! empty($token):
--- a/src/Illuminate/Auth/TokenGuard.php
+++ b/src/Illuminate/Auth/TokenGuard.php
@@ -80,7 +80,7 @@ public function user()
$token = $this->getTokenForRequest();
- if (! empty($token)) {
+ if (is_string($token) && ! empty($token)) {
$user = $this->provider->retrieveByCredentials([
$this->storageKey => $this->hash ? hash('sha256', $token) : $token,
]);
@@ -110,7 +110,7 @@ public function getTokenForRequest()
*/
public function validate(array $credentials = [])
{
- if (empty($credentials[$this->inputKey])) {
+ if (! is_string($credentials[$this->inputKey] ?? null) || empty($credentials[$this->inputKey])) {
return false;
}
When $this->hash is false and $token is [0], TokenGuard passes ['api_token' => [0]] to EloquentUserProvider::retrieveByCredentials(). Inside retrieveByCredentials(), $query->where('api_token', [0]) flattens the single-element array into the first positional binding of SELECT * FROM users WHERE api_token = ? LIMIT 1 with bound integer 0.
In MySQL and MariaDB, comparing a VARCHAR or CHAR column (api_token) against an integer literal 0 converts the stored string to a floating-point number from left to right. Any stored token that begins with a non-digit character (a..z, A..Z) evaluates to 0 ('a8f5f167...' = 0 is TRUE), so the query returns the first matching user row in the table and authenticates the request as that user. Note that if 'hash' => true is enabled in PHP 8+, calling hash('sha256', [0]) throws a TypeError instead of authenticating, which is why the advisory specifically scopes the bypass to unhashed TokenGuard setups (or custom providers that accept array credentials).
2. CVE-2026-114508: JsonApiResource nested include calls truncate on related tables
Laravel’s JsonApiResource (Illuminate\Http\Resources\JsonApi\JsonApiResource) supports sparse relationship inclusion through the ?include= query parameter (for example, GET /users/1?include=posts,comments).
In laravel/framework 12.45.0 through 12.67.x and 13.0.0 through 13.26.x, compileResourceRelationships() in src/Illuminate/Http/Resources/JsonApi/Concerns/ResolvesJsonApiElements.php validated the top-level relationship name against $this->toRelationships($request), resolved $relatedModels, and then immediately passed any dot-prefixed child segments ($request->sparseIncluded($relationName)) to $relatedModels->loadMissing($relations):
// src/Illuminate/Http/Resources/JsonApi/Concerns/ResolvesJsonApiElements.php (vulnerable)
foreach ($resourceRelationships as $relationName => $relationResolver) {
$relatedModels = $relationResolver->handle($this->resource);
$relatedResourceClass = $relationResolver->resourceClass();
if (! is_null($relatedModels) && $this->includesPreviouslyLoadedRelationships === false) {
if (! empty($relations = $request->sparseIncluded($relationName))) {
$relatedModels->loadMissing($relations);
}
}
While the top-level $relationName (comments) was restricted to the allowlist returned by toRelationships(), the nested segment after the dot (truncate in ?include=comments.truncate or posts.comments.truncate) was never checked against the child JsonApiResource’s toRelationships() map before loadMissing(['truncate']) ran on the Eloquent collection. When Eloquent eager-loads a relation name on a model, it calls $model->{$name}() and invokes constraints on the returned object; because Eloquent models forward unknown methods to a new Eloquent\Builder instance via __call(), calling $comment->truncate() executes TRUNCATE TABLE comments directly against the database during a GET request.
Commit 50a7970efe (PR #61323 for 12.68.0, and commit b89f294499 / PR #61322 for 13.27.0) removes the unvalidated $relatedModels->loadMissing($relations) call and instead passes $requestedRelationships down to each child JsonApiResource, which filters every level against its own declared toRelationships($request) map.
3. GHSA-mhf7-xp6r-6qrc: Livewire #[Authorize] parameter key confusion and #[Json] ordering
Livewire 4 (>= 4.3.0, < 4.4.8) introduced the #[Authorize('ability', 'modelParam')] method attribute to run Laravel Gate policy checks before executing component actions. Commit 822a4fa9b1 fixes three flaws in how that attribute interacted with Livewire’s wire protocol:
- Out-of-order integer keys on component actions (
HandleComponents.php): When#[Authorize('delete', 'post')]inspected the incomingparamsarray for the first parameter (index 0), it looked up$params[0]by array key. When Livewire later invokeddeletePost(Post $post), PHP spread$paramsin iteration order ($component->{$method}(...$params)). In PHP, an associative JSON object"params": {"1": 2, "0": 1}preserves insertion order (2first,1second) while$params[0]evaluates to1. A user who owned post1could therefore pass the Gate check on post1whiledeletePost()received post2as its first positional argument. - The same key-order mismatch on
#[On]event listeners (SupportEvents.php): Calling__dispatchwith["delete-post", {"1": 2, "0": 1}]triggered the identical mismatch on methods decorated with both#[On]and#[Authorize]. #[Json]methods executed before#[Authorize](SupportJson.php): Livewire’sSupportJsoncallhook intercepted methods marked with#[Json], ran the method body immediately, and called$returnEarly(), before the regularBaseAuthorizemethod-level attribute hook had a chance to run. Even though the response later returned HTTP403, any database write or deletion inside the#[Json]method had already committed.
Livewire 4.4.8 adds Utils::hasUnambiguousParameterKeys() to reject any params array that is neither a sequential list (array_is_list($params)) nor an all-string-keyed map, and runs all method-level BaseAuthorize attributes at the top of SupportJson’s call listener:
// src/Drawer/Utils.php in livewire/livewire 4.4.8
static function hasUnambiguousParameterKeys(array $params)
{
return array_is_list($params) || array_filter(array_keys($params), 'is_int') === [];
}
Detection and remediation
1. Audit installed versions and search for affected patterns
Run the following commands across your PHP repositories and container images:
# Check installed versions in composer.lock
composer show laravel/framework livewire/livewire 2>/dev/null || true
rg -n -A 3 '"name":\s*"(laravel/framework|livewire/livewire)"' composer.lock 2>/dev/null
# 1. Check config/auth.php for guards using the 'token' driver without 'hash' => true (CVE-2026-114509)
rg -n "'driver'\s*=>\s*'token'" config/ 2>/dev/null
# 2. Check for JsonApiResource usage with toRelationships() (CVE-2026-114508)
rg -n 'extends\s+JsonApiResource|use\s+Illuminate\\Http\\Resources\\JsonApi\\JsonApiResource|toRelationships\s*\(' app/ src/ 2>/dev/null
# 3. Check for Livewire 4 #[Authorize] attributes (GHSA-mhf7-xp6r-6qrc)
rg -n '#\[Authorize\b' app/ src/ 2>/dev/null
2. Upgrade packages and apply defense-in-depth
- Upgrade
laravel/frameworkandlivewire/livewire:
Ensurecomposer update laravel/framework livewire/livewire --with-dependencieslaravel/frameworkresolves to>= 12.68.0(on 12.x) or>= 13.29.0(on 13.x), andlivewire/livewireresolves to>= 4.4.8. - Migrate legacy
TokenGuardendpoints to hashed tokens or Laravel Sanctum: Even on patched Laravel versions, storing plaintext API tokens in the database ('hash' => false) exposes every user if theuserstable is read. Enable'hash' => trueinconfig/auth.phpor migrate token authentication to Laravel Sanctum. - If you cannot upgrade Livewire 4 immediately: Call
$this->authorize('delete', $model)directly inside the component method body rather than relying on the#[Authorize]attribute untilv4.4.8is deployed.
Timeline
- 2026-10-13: Laravel merges
JsonApiResourcerelationship-inclusion fixes in PRs#61322(b89f294499) and#61323(50a7970efe) and shipsv12.68.0andv13.27.0. - 2026-10-15: Laravel commits the
TokenGuardis_string()check (8dbb08bcca/9b21ce0a9b) and shipsv13.29.0(backported tov12.68.0). - 2026-10-21: Livewire merges commit
822a4fa9b1and releaseslivewire/livewirev4.4.8; NVD publishesCVE-2026-114508andCVE-2026-114509. - 2026-10-23 (20:51 to 21:50 UTC): GitHub publishes the reviewed advisories
GHSA-mhf7-xp6r-6qrc,GHSA-5wcm-3v2w-8362(CVE-2026-114508), andGHSA-f9x3-7vq9-cph6(CVE-2026-114509, rated CVSS v4.0 9.2 Critical).
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- Laravel advisory GHSA-f9x3-7vq9-cph6 (CVE-2026-114509: TokenGuard auth bypass)
- laravel/framework commit 8dbb08bcca (require string token in TokenGuard)
- Laravel advisory GHSA-5wcm-3v2w-8362 (CVE-2026-114508: relationship inclusion data destruction)
- laravel/framework PR #61323 / commit 50a7970efe (scope nested JsonApiResource includes to defined resource relationships)
- Livewire advisory GHSA-mhf7-xp6r-6qrc (#[Authorize] parameter key order and #[Json] bypass)
- livewire/livewire commit 822a4fa9b1 (enforce unambiguous parameter keys and pre-run #[Json] authorization)
- NVD: CVE-2026-114509 and CVE-2026-114508