Laravel: gửi số 0 thay cho token là đăng nhập được vào tài khoản người khác
Lỗi CVE-2026-114509 (CVSS 9,2) trong guard token của Laravel cho người lạ đăng nhập API bằng tài khoản có sẵn, thường là admin, nếu app dùng MySQL hoặc MariaDB. Đã có mã khai thác công khai. Nâng lên 12.68.0 hoặc 13.29.0 và kiểm tra config/auth.php.
Xem nhanh trong 1 phút
Token API của bạn dài 60 ký tự ngẫu nhiên, đoán cả đời không ra. Nhưng với một app Laravel chạy MySQL, kẻ tấn công không cần đoán. Họ gửi {"api_token": [0]} và database trả về một user có thật, thường là user tạo đầu tiên, tức tài khoản admin.
Đó là lỗ hổng CVE-2026-114509 trong guard token của Laravel, được chấm 9,2/10 theo CVSS v4 (GitLab chấm 9,8 theo CVSS 3.1). Laravel công bố khuyến cáo ngày 19/10. Ngay sau đó, nhóm Hakai Security đưa lên GitHub một bản PoC kèm môi trường Docker dựng sẵn, và thêm ít nhất một kho PoC khác xuất hiện. Đến sáng 24/10 chưa có nguồn nào xác nhận lỗi bị khai thác thật, nhưng mã khai thác đã nằm sẵn đó cho ai muốn thử.
Lỗi nằm ở đâu
Guard token là cách xác thực API đơn giản nhất của Laravel: bảng users có cột api_token, client gửi token lên, Laravel tìm user có token khớp. Rất nhiều bài hướng dẫn cũ dùng cách này, và AI viết code Laravel cũng có lúc chọn nó vì ít bước cài.
Trước bản vá, lớp TokenGuard chỉ kiểm token có rỗng không. Nó không kiểm token có phải là chuỗi hay không. Nếu client gửi một mảng, mảng đó đi thẳng xuống EloquentUserProvider, và hàm này thấy giá trị là mảng thì dựng câu truy vấn WHERE api_token IN (...).
Phần còn lại do MySQL. Khi so một cột chữ với một số, MySQL đổi chữ sang số. Chuỗi bắt đầu bằng chữ cái, như aB3x..., đổi ra thành 0. Vậy WHERE api_token IN (0) khớp với mọi token bắt đầu bằng chữ cái, và Laravel lấy user đầu tiên trong số đó.
Theo mô tả trong kho PoC:
Phải gửi bằng body JSON (POST, PUT, PATCH), vì JSON giữ được số nguyên 0. Gửi ?api_token[]=0 trên URL thì 0 thành chuỗi "0", không khớp.
Kẻ tấn công không chọn được người, chỉ lấy được user khớp đầu tiên. Với app mới dựng, đó thường là tài khoản admin.
PostgreSQL và SQLite không bị, vì hai database này không tự đổi kiểu như vậy.
Bật 'hash' => true cho guard thì trên PHP 8 Laravel gọi hash() với một mảng và báo lỗi, nên không lọt. Khuyến cáo chính thức không nhắc tới chi tiết này.
Bản vá chỉ là một dòng: if (is_string($token) && ! empty($token)). Thiếu đúng dòng kiểm kiểu đó, người lạ vào được API bằng tài khoản admin.
Ai bị ảnh hưởng
Bạn cần lo nếu cả ba điều sau đều đúng:
laravel/framework thấp hơn 12.68.0 (nhánh 12) hoặc thấp hơn 13.29.0 (nhánh 13)
config/auth.php có guard dùng 'driver' => 'token'
Bảng user nằm trên MySQL hoặc MariaDB
Sanctum và Passport không bị, vì hai gói này có guard riêng. Phần lớn app Laravel mới dùng Sanctum, nên đừng hoảng nếu bạn chỉ dùng auth:sanctum.
Chuyện lạ của vụ này: hai bản 12.68.0 và 13.29.0 đã phát hành từ 25/8/2026, gần hai tháng trước khi khuyến cáo ra. App nào cập nhật thường xuyên thì đã được vá từ lâu mà không biết. Còn app khóa cứng phiên bản từ đầu năm thì đang hở.
Laravel 11 trở xuống không có trong danh sách bản vá chính thức. Kho PoC có nhắc bản 11.56.1, nhưng khuyến cáo của Laravel chỉ liệt kê 12.68.0 và 13.29.0. Còn chạy Laravel 11 hay cũ hơn thì nên tính chuyện lên nhánh 12.
Kiểm tra app của bạn
Ở thư mục dự án, xem phiên bản đang chạy:
composer show laravel/framework | grep versionscomposer audit
composer audit sẽ báo GHSA-f9x3-7vq9-cph6 nếu bạn đang ở bản lỗi. Tiếp theo, tìm guard token:
Rồi deploy lại. Trên máy chủ chạy PHP-FPM hay Octane, khởi động lại tiến trình để code mới được nạp.
Chưa nâng cấp được ngay, bạn có thể chặn tạm bằng một middleware đặt trước guard, từ chối mọi request có api_token không phải chuỗi:
// app/Http/Middleware/ApiTokenPhaiLaChuoi.phppublic function handle($request, Closure $next){ if ($request->has('api_token') && ! is_string($request->input('api_token'))) { abort(401); } return $next($request);}
Đây là cách vá tạm do mình đề xuất, Laravel không đưa ra cách nào ngoài nâng cấp. Nâng cấp xong thì bỏ nó đi.
Nếu app đã mở ra internet một thời gian với cấu hình dính lỗi, hãy xem log truy cập các route API có request nào gửi body JSON chứa "api_token":[ không, và tạo lại token cho các tài khoản quản trị.
Cùng một kiểu lỗi, ở code bạn tự viết
Lỗi này không riêng gì Laravel. Bất cứ chỗ nào lấy thẳng giá trị từ body JSON rồi đưa vào câu truy vấn mà không kiểm kiểu đều có thể bị tương tự. Ví dụ quen thuộc với Express và MongoDB: gửi {"token": {"$ne": null}} thì User.findOne({ token: req.body.token }) trả về user đầu tiên có token.
Cách chặn ở mọi ngôn ngữ đều giống nhau: trước khi tra database, kiểm token là chuỗi, đúng độ dài bạn cấp ra. Với Node.js, một dòng if (typeof token !== 'string') return res.sendStatus(401) là đủ. Dùng thư viện kiểm dữ liệu như Zod, Pydantic hay Form Request của Laravel thì khai báo rõ kiểu string cho trường token.
Khi nhờ AI viết phần đăng nhập API, bạn có thể dặn thêm một câu: “kiểm kiểu dữ liệu của token trước khi truy vấn”. Câu dặn đó rẻ hơn nhiều so với một tài khoản admin bị chiếm.
App Laravel của bạn đang xác thực API bằng gì, token cũ, Sanctum hay tự viết? Vào cộng đồng Vibe Code & Bảo Mật kể cho mọi người nghe nhé.
Laravel: gui so 0 thay cho token la dang nhap duoc vao tai khoan nguoi khac Loi CVE-2026-114509 (CVSS 9,2) trong guard token cua Laravel cho nguoi la dang nhap API bang tai khoan co san, thuong la admin, neu app dung MySQL hoac MariaDB. Da co ma khai thac cong khai. Nang len 12.68.0 hoac 13.29.0 va kiem tra config/auth.php. lo hong dang nhap API database Token API cua ban dai 60 ky tu ngau nhien, doan ca doi khong ra. Nhung voi mot app Laravel chay MySQL, ke tan cong khong can doan. Ho gui `{"api_token": [0]}` va database tra ve mot user co that, thuong la user tao dau tien, tuc tai khoan admin.

Do la lo hong **CVE-2026-114509** trong guard `token` cua Laravel, duoc cham 9,2/10 theo CVSS v4 (GitLab cham 9,8 theo CVSS 3.1). Laravel cong bo khuyen cao ngay 19/10. Ngay sau do, nhom Hakai Security dua len GitHub mot ban PoC kem moi truong Docker dung san, va them it nhat mot kho PoC khac xuat hien. Den sang 24/10 chua co nguon nao xac nhan loi bi khai thac that, nhung ma khai thac da nam san do cho ai muon thu.
## Loi nam o dau
Guard `token` la cach xac thuc API don gian nhat cua Laravel: bang `users` co cot `api_token`, client gui token len, Laravel tim user co token khop. Rat nhieu bai huong dan cu dung cach nay, va AI viet code Laravel cung co luc chon no vi it buoc cai.
Truoc ban va, lop `TokenGuard` chi kiem token co rong khong. No khong kiem token co phai la chuoi hay khong. Neu client gui mot **mang**, mang do di thang xuong `EloquentUserProvider`, va ham nay thay gia tri la mang thi dung cau truy van `WHERE api_token IN (...)`.
Phan con lai do MySQL. Khi so mot cot chu voi mot **so**, MySQL doi chu sang so. Chuoi bat dau bang chu cai, nhu `aB3x...`, doi ra thanh 0. Vay `WHERE api_token IN (0)` khop voi moi token bat dau bang chu cai, va Laravel lay user dau tien trong so do.
Theo mo ta trong kho PoC:
- Phai gui bang **body JSON** (POST, PUT, PATCH), vi JSON giu duoc so nguyen 0. Gui `?api_token[]=0` tren URL thi 0 thanh chuoi `"0"`, khong khop.
- Ke tan cong khong chon duoc nguoi, chi lay duoc user khop dau tien. Voi app moi dung, do thuong la tai khoan admin.
- PostgreSQL va SQLite khong bi, vi hai database nay khong tu doi kieu nhu vay.
- Bat `'hash' => true` cho guard thi tren PHP 8 Laravel goi `hash()` voi mot mang va bao loi, nen khong lot. Khuyen cao chinh thuc khong nhac toi chi tiet nay.
Ban va chi la mot dong: `if (is_string($token) && ! empty($token))`. Thieu dung dong kiem kieu do, nguoi la vao duoc API bang tai khoan admin.
## Ai bi anh huong
Ban can lo neu ca ba dieu sau deu dung:
- `laravel/framework` thap hon **12.68.0** (nhanh 12) hoac thap hon **13.29.0** (nhanh 13)
- `config/auth.php` co guard dung `'driver' => 'token'`
- Bang user nam tren MySQL hoac MariaDB
**Sanctum va Passport khong bi**, vi hai goi nay co guard rieng. Phan lon app Laravel moi dung Sanctum, nen dung hoang neu ban chi dung `auth:sanctum`.
Chuyen la cua vu nay: hai ban 12.68.0 va 13.29.0 da phat hanh tu **25/8/2026**, gan hai thang truoc khi khuyen cao ra. App nao cap nhat thuong xuyen thi da duoc va tu lau ma khong biet. Con app khoa cung phien ban tu dau nam thi dang ho.
Laravel 11 tro xuong khong co trong danh sach ban va chinh thuc. Kho PoC co nhac ban 11.56.1, nhung khuyen cao cua Laravel chi liet ke 12.68.0 va 13.29.0. Con chay Laravel 11 hay cu hon thi nen tinh chuyen len nhanh 12.
## Kiem tra app cua ban
O thu muc du an, xem phien ban dang chay:
```bash
composer show laravel/framework | grep versions
composer audit
```
`composer audit` se bao GHSA-f9x3-7vq9-cph6 neu ban dang o ban loi. Tiep theo, tim guard `token`:
```bash
grep -n "'driver' => 'token'" config/auth.php
```
Co ket qua va dang o ban loi thi nang cap:
```bash
composer update laravel/framework
php artisan --version
```
Roi deploy lai. Tren may chu chay PHP-FPM hay Octane, khoi dong lai tien trinh de code moi duoc nap.
Chua nang cap duoc ngay, ban co the chan tam bang mot middleware dat truoc guard, tu choi moi request co `api_token` khong phai chuoi:
```php
// app/Http/Middleware/ApiTokenPhaiLaChuoi.php
public function handle($request, Closure $next)
{
if ($request->has('api_token') && ! is_string($request->input('api_token'))) {
abort(401);
}
return $next($request);
}
```
Day la cach va tam do minh de xuat, Laravel khong dua ra cach nao ngoai nang cap. Nang cap xong thi bo no di.
Neu app da mo ra internet mot thoi gian voi cau hinh dinh loi, hay xem log truy cap cac route API co request nao gui body JSON chua `"api_token":[` khong, va tao lai token cho cac tai khoan quan tri.
## Cung mot kieu loi, o code ban tu viet
Loi nay khong rieng gi Laravel. Bat cu cho nao lay thang gia tri tu body JSON roi dua vao cau truy van ma khong kiem kieu deu co the bi tuong tu. Vi du quen thuoc voi Express va MongoDB: gui `{"token": {"$ne": null}}` thi `User.findOne({ token: req.body.token })` tra ve user dau tien co token.
Cach chan o moi ngon ngu deu giong nhau: truoc khi tra database, kiem token la chuoi, dung do dai ban cap ra. Voi Node.js, mot dong `if (typeof token !== 'string') return res.sendStatus(401)` la du. Dung thu vien kiem du lieu nhu Zod, Pydantic hay Form Request cua Laravel thi khai bao ro kieu `string` cho truong token.
Khi nho AI viet phan dang nhap API, ban co the dan them mot cau: "kiem kieu du lieu cua token truoc khi truy van". Cau dan do re hon nhieu so voi mot tai khoan admin bi chiem.
---
App Laravel cua ban dang xac thuc API bang gi, `token` cu, Sanctum hay tu viet? Vao [cong dong Vibe Code & Bao Mat](https://www.facebook.com/groups/1867387324580254) ke cho moi nguoi nghe nhe.
## Nguon
- [Token-based API guard auth bypass (GHSA-f9x3-7vq9-cph6)](https://github.com/laravel/framework/security/advisories/GHSA-f9x3-7vq9-cph6), khuyen cao bao mat cua Laravel tren GitHub
- [CVE-2026-114509](https://advisories.gitlab.com/composer/laravel/framework/CVE-2026-114509/), GitLab Advisory Database
- [Commit sua loi "fix token retrieval"](https://github.com/laravel/framework/commit/8dbb08bccac1be095dba4a9d259a419620f1e8a5), laravel/framework
- [Laravel Token Guard Auth Bypass](https://laravel-news.com/laravel-token-guard-auth-bypass), Laravel News
- [Laravel Authentication Bypass PoC Released for Critical TokenGuard Flaw](https://securityonline.info/laravel-authentication-bypass-cve-2026-114509-poc/), Daily CyberSecurity
- [CVE-2026-114509 PoC](https://github.com/tajulasri/-CVE-2026-114509-POC), kho PoC tren GitHub