Laravel audit patterns
TL;DR: Laravel’s Eloquent ORM is mostly safe; the bugs hide in
DB::raw/whereRaw, mass assignment via missing$fillable/$guarded, Blade{!! !!}unescaped echoes,unserializeon session/cache,APP_KEYleak, debug endpoints (Telescope/Horizon/Ignition), and SSRF viaHttp::getuser input. CVE-2021-3129 (Ignition + debug = RCE) is the modern poster child.
Common bug patterns
1. SQL injection via raw helpers
DB::raw("name = '$name'")→ SQLi.User::whereRaw("name = '" . $name . "'")→ SQLi.User::orderBy($request->sort)— order-by SQLi (column name not parameterised, validated against allowlist?).whereColumn,wherefirst-arg as raw expression — read carefully.- Safe:
User::where('name', $name),whereRaw('name = ?', [$name]).
2. Mass assignment
- Models default to
$fillable = [](protected). But many devs set$guarded = [](allow all) — that’s mass-assign-anything. User::create($request->all())with$guarded = []→ attacker setsis_admin. Use$request->validated()(with FormRequest) or explicit array.Model::forceFill(...)bypasses guards entirely; audit anywhere it’s used with request data.
3. Blade unescaped echo
{{ $x }}escapes.{!! $x !!}does not — XSS surface.@phpblocks execute PHP — audit for user input concatenation.- Custom Blade directives via
Blade::directiveare templates compiled to PHP — if directive logic embeds user input into compiled output, SSTI.
4. Session / cookie deserialization
- Default Laravel session driver
cookieencrypts viaAPP_KEYthenunserializes on decrypt. IfAPP_KEYleaks (env file in repo,.env.examplemistake, debug page), attacker crafts cookie →unserialize→ POP chain → RCE. - See php-deserialization-gadgets.
5. APP_KEY leak chains
.envfile in webroot (misconfigured nginx/Apache).php artisan tinkerexposed (no).- Ignition debug error page (CVE-2021-3129) leaks env via “Make Variable Optional” feature → leak
APP_KEY→ forge session cookie → POP → RCE. - Check
.env/.env.localingit log -- .env*and CI logs.
6. Debug / admin packages
laravel/telescopein prod with unauthenticated access — leaks every request, every job, every Redis command, every mail.laravel/horizon/horizonUI same risk.barryvdh/laravel-debugbarin prod = SQL queries + bindings on every page.facade/ignition<2.5.2 = RCE (CVE-2021-3129).
7. SSRF
Http::get($url),Http::withBody(...)->post($url)with user-controlled$url. No SSRF guard in stdlib.Guzzledirect use — same. Add IP allowlist via customHandler.- File preview endpoints that fetch URLs server-side are the classic SSRF surface in Laravel apps.
8. File upload
$request->file('img')->store('uploads')keeps original extension; rename via hash + validate MIME viamimes:jpeg,pngrule, not extension.storage:linkexposesstorage/app/publicto webroot → user uploads served. If MIME is text/html or svg → stored XSS.
9. Authorization gaps
- Policies via
Gate::defineorPolicyclasses are opt-in per route.$this->authorize(...)must be called in the controller — missing = no check. Route::resourcedoesn’t auto-call policies; FormRequest authorization (authorize(): bool) is per-request, not per-resource.
10. Eloquent IDOR
User::find($id)returns any matching row regardless of ownership. Pair with policy orwhere('owner_id', auth()->id()).- Route-model binding (
function (Post $post)) skips ownership; add$this->authorize('view', $post).
11. Queue serialization
- Jobs serialised to queue via
serialize(); cookie/session same. If queue worker reads from untrusted source (cross-tenant Redis) → POP chain.
12. Storage::disk('s3')->url(...) SSRF
- AWS SDK creates presigned URL when configured; not a direct sink, but
getObjectwith user-controlled key can list outside intended prefix ifprefixnot constrained.
Grep starter
1
2
3
4
5
6
rg -n 'DB::raw|whereRaw|havingRaw|orderByRaw|selectRaw' .
rg -n '\$guarded\s*=\s*\[\]|->forceFill\(' .
rg -n '\{!!.*\$|\{!!.*\$request' resources/views
rg -n 'APP_DEBUG\s*=\s*true|debugbar|telescope|horizon|ignition' .
rg -n 'Http::(get|post|put|delete)\(\s*\$request->' .
rg -n 'unserialize\(|serialize\(.*\$request' .
Tooling
- Larastan (PHPStan for Laravel).
- Enlightn Security Checker — Laravel-specific.
composer audit(Composer 2.4+).- Snyk has good Laravel CVE coverage.
References
- Laravel security topic
- Enlightn — opinionated security audit
- Spatie research blog — Laravel package security writeups
- CVE-2021-3129 — Ignition RCE writeup (AmbionicsLabs)
- See also: php-code-auditing, php-deserialization-gadgets, php-magic-methods