Key Takeaways
- A May 2026 Packagist attack showed that a version number alone can’t prove a Composer install is clean.
- Laravel 13 is fully supported, Laravel 12 is on security fixes only, and everything older is end of life.
- CVE-2026-48019 affects every Laravel release below 12.60.0, plus Laravel 13 before 13.10.0.
- Most Laravel risk lives in the opt-outs, the raw queries, and the dependency nobody updated.
Laravel security has less to do with the framework and more to do with what happens after you install it. One disabled default, one raw query, or one dependency nobody updated is usually enough to open a hole.
This guide walks through the threats still landing in production, the practices that close them, how to secure your APIs, and what to do if you’re stuck running an end-of-life version.
Why Security Matters in Laravel Applications
A single breach can undo years of customer trust, trigger legal liability, and hand attackers the server credentials that open up everything else you run. And the way in has shifted. The 2026 Verizon DBIR found that exploiting a vulnerability is now the most common initial access vector for breaches, showing up in 31% of them, against 20% the year before, while credential abuse fell to 13%.
Laravel does close off whole classes of problems out of the box. Eloquent parameterizes query values, Blade escapes output by default, and the request forgery middleware sits in the web group without anyone asking for it. None of that survives a developer switching it off, and none of it covers authorization, misconfiguration, server-side request forgery, business logic, or a vulnerable dependency. Those stay your job, permanently.
Common Laravel Security Threats
Cross-Site Scripting via Unescaped Blade Output
Blade’s {{ }} syntax escapes output automatically. The {!! !!} syntax doesn’t, and it’s what people reach for when they need to render stored HTML from a CMS field or a rich-text editor.
If that stored HTML includes a script tag, it runs in the browser of everyone who loads the page. Sanitize it server-side first, with a library like HTMLPurifier, before it ever reaches {!! !!}.
SQL Injection Through Raw Expressions
Eloquent and the query builder parameterize query values, so injection isn’t a worry for ordinary queries. It comes straight back the moment user input gets concatenated into DB::raw(), whereRaw(), havingRaw(), or orderByRaw().
Column and direction names are the usual miss, since they can’t be bound as parameters. Allowlist them against a fixed array instead. Nothing about this is Laravel-specific, either. It’s the same mistake PHP developers have been making with string-built SQL for twenty years.
Broken Authentication and Session Handling
Laravel’s auth scaffolding is solid. The failures happen around it: no rate limiting on login, reset tokens that never expire, sessions that don’t regenerate after a privilege change, and custom change-password flows that skip setRememberToken, leaving remember-me sessions valid after the password changes.
Check authorization separately from authentication. A working login that lets any authenticated user hit /admin is an authorization vulnerability, not a bug in the login form.
Laravel Security Best Practices
1. Keep Laravel and Its Dependencies Patched
Run composer outdated every week or two to see which packages have newer releases, then composer update to pull them in within whatever constraints you’ve set in composer.json. Keep an eye on Laravel’s release notes and GitHub security advisories too, because that’s where a newly disclosed framework vulnerability shows up first.
Laravel’s current policy gives each release 18 months of bug fixes and two years of security fixes from its release date. There’s no LTS tier anymore either. Laravel 6 was the last one, and the windows moved around during the shift from six-month minor releases to annual majors, which is why the older rows below don’t all follow the same shape.
| Version | Release Date | Bug Fixes Until | Security Fixes Until |
| 13 | March 17, 2026 | Q3 2027 | March 17, 2028 |
| 12 | February 24, 2025 | August 13, 2026 | February 24, 2027 |
| 11 | March 12, 2024 | September 3, 2025 | March 12, 2026 |
| 10 | February 14, 2023 | August 6, 2024 | February 4, 2025 |
| 9 | February 8, 2022 | August 8, 2023 | February 6, 2024 |
| 8 | September 8, 2020 | July 26, 2022 | January 24, 2023 |
| 7 | March 3, 2020 | October 6, 2020 | March 3, 2021 |
| 6 (LTS) | September 3, 2019 | January 25, 2022 | September 6, 2022 |
| 5.8 | February 26, 2019 | August 26, 2019 | February 26, 2020 |
| 5.7 | September 4, 2018 | March 4, 2019 | September 4, 2019 |
| 5.6 | February 7, 2018 | August 7, 2018 | February 7, 2019 |
| 5.5 (LTS) | August 30, 2017 | August 30, 2019 | August 30, 2020 |
| 5.4 | January 24, 2017 | July 24, 2017 | January 24, 2018 |
CVE-2026-48019 shows the cost of falling behind. It’s a CRLF injection in Laravel’s default email validation rule that, combined with how Symfony Mailer and Symfony Mime treat certain character sequences, lets an unauthenticated attacker tamper with outbound mail, slipping in a Bcc header, altering message content, or turning the app into a relay. The advisory marks every release below 12.60.0 as affected, along with Laravel 13 up to 13.9.0, and the fixes landed in 12.60.0 and 13.10.0. Laravel 11’s security window had already closed in March 2026, so 11 and every branch below it sits inside that range with no patched release from upstream.
If a major upgrade isn’t realistic right now, TuxCare’s Endless Lifecycle Support for Laravel backports security fixes through Composer to specific baselines, currently 5.4.36 through 12.58.0, with other versions available on request.
2. Treat Every Protection Opt-Out as a Code Review Gate
Every Laravel security default ships with an escape hatch, and each one deserves a second pair of eyes before it goes out. {!! !!} in Blade turns off output escaping. DB::raw() and whereRaw() accept raw SQL, so any untrusted input has to arrive as a binding rather than concatenated into the string. Model::unguard(), $guarded = [],and Laravel 13’s #[Unguarded] attribute all open mass assignment on every column.
Request forgery protection has its own escape hatch, and the API has moved. On Laravel 13 you exclude routes with preventRequestForgery(except: […]) in bootstrap/app.php, on 11 and 12 the same method is called validateCsrfTokens(), and anything older uses the $except property on the CSRF middleware class. A route can also opt out by passing the relevant request forgery middleware class to withoutMiddleware().
Add all of these to a grep-based CI check, and make the author of any pull request that introduces one explain in the description why it’s there.
3. Validate Server-Side and Authorize Separately
Validation checks whether the data is well-formed. Authorization checks whether this user is allowed to act on it. Passing one says nothing about the other.
Use form request classes so validation rules live somewhere reviewable, and Policies or Gates for every model action. Client-side validation improves the user experience and does nothing whatsoever for security.
4. Lock Down Configuration and Secrets
Set APP_DEBUG=false in production, without exception. Laravel’s own documentation is blunt about the risk of leaving debug mode on: sensitive configuration values can be exposed to end users, and the debug error page serves detailed stack traces to whoever triggered the error.
Keep .env out of version control, confirm your web root points at /public, and rotate APP_KEY if it has ever been committed. Every encrypted value and signed cookie in the application depends on that key, so list the old key in APP_PREVIOUS_KEYS when you rotate. Without it, previously encrypted data can no longer be decrypted, and every authenticated session is logged out.
5. Audit Dependencies on a Schedule, Not After Incidents
Run composer audit in CI so a known-vulnerable package fails the build instead of surfacing in a quarterly report.
composer audit –format=summary
Laravel’s first-party packages get CVEs too. CVE-2026-39976 was an authentication bypass in laravel/passport, fixed in 13.7.1, where a client_credentials token could resolve to a real user account. It only bites if UUID client IDs have been turned off, which is exactly the sort of detail that gets lost when a scanner just tells you a package is flagged. Pair scheduled scanning with regular security audits and a documented process for vulnerability patching.
Watch Out: On May 22–23, 2026, an attacker republished malicious code under historical release tags for four Laravel-Lang packages on Packagist. Every existing tag was repointed to a new commit, so the version number in your composer.json told you nothing about what had actually been installed. Working out whether you were hit meant checking the resolved commit in composer.lock, when your builds ran, and the indicators of compromise published at the time.
Securing Laravel APIs
Most APIs are stateless and token-based, which changes the security model rather than simply weakening it. A bearer token isn’t attached automatically by the browser the way a session cookie is, so CSRF protection stops being the control that matters. What carries the weight instead is authentication, authorization on every action, a CORS policy somebody actually configured, rate limiting, and token lifecycle. Stateful setups like Sanctum’s SPA mode are the exception, since they do use session cookies and do need CSRF protection.
One thing genuinely is missing, though. Nobody is sitting there watching the response, so a slow enumeration run against an API can keep going for days before anyone notices.
Choose Sanctum or Passport Deliberately
Sanctum handles token and SPA authentication with minimal setup and fits most applications. Passport implements full OAuth2 and makes sense only when you genuinely need third-party authorization flows.
Passport’s larger surface carries more risk. Whichever you choose, scope tokens narrowly and set expirations. A token with no expiry stays valid until somebody remembers to revoke it, which makes a leak very hard to contain.
Rate Limit Every Public Endpoint
Laravel’s throttle middleware is one line, and it’s easy to leave off an API route. Without it, credential stuffing and enumeration run at whatever speed your server allows.
Rate limit per user and per IP, with tighter limits on login, password reset, and anything that sends email or SMS. Return a 429 rather than failing silently.
Harden the Layer Below Your Application
An API is only as secure as the host serving it. Enforce TLS, disable directory listings, and patch the OS and PHP runtime independently of the Laravel release cycle.
Application-level security doesn’t survive a compromised server. Whatever you do inside the Laravel codebase, the box underneath it needs hardening and patching on its own terms.
Note: Laravel 13 formalized request forgery protection as PreventRequestForgery, which adds an origin check on the browser’s Sec-Fetch-Site header alongside the usual token validation. A same-origin request is allowed without a token, and anything that doesn’t clear the origin check falls through to token validation instead. Laravel’s docs note that the header only arrives over HTTPS, so plain HTTP still leans entirely on the token. Read what the middleware actually checks before you exempt anything from it.
Laravel Security Vendors and Solutions
TuxCare Endless Lifecycle Support
ELS backports security fixes to Laravel versions the core team no longer maintains, delivered through Composer, so the dependency workflow doesn’t change. TuxCare covers EOL PHP runtimes too, which matters when an older Laravel app is also sitting on a PHP version that upstream stopped patching.
ELS isn’t a substitute for eventually upgrading. It’s long-term security support for branches upstream has stopped patching , so you keep receiving CVE fixes for an EOL branch and plan the migration on your own schedule rather than somebody else’s.
We currently support these Laravel versions: 5.4.36, 5.5.50, 5.6.40, 5.7.29, 5.8.38, 6.20.45, 7.30.7, 8.12.0, 8.12.1, 8.12.2, 8.12.3, 8.83.29, 9.52.21, 10, 11, 12.58.0.
Dependency Scanning
Start with composer audit, because it’s free and already installed. It audits your installed packages, so transitive dependencies are in scope as well, and composer audit –locked checks the lock file instead when the vendor directory isn’t present. Snyk, GitHub Dependabot, and Aikido layer continuous monitoring, alerting, and automated update pull requests on top of that, which is worth having when nobody is watching CI closely. For a smaller team, Dependabot is a reasonable baseline. What none of them solve is an EOL package, where there’s no fixed version to upgrade to in the first place.
Runtime Protection and Monitoring
A web application firewall buys time between disclosure and patch, nothing more. Cloudflare, AWS WAF, and ModSecurity with a maintained ruleset such as the OWASP Core Rule Set can stop many common injection and XSS payloads before they reach PHP, though coverage depends entirely on the rules you turn on. Pair the firewall with log monitoring so an exploitation attempt looks different from an ordinary bad request.
Secure Laravel Applications with TuxCare
Keeping a Laravel app inside its support window is where teams fall behind. Major upgrades bring breaking changes, and they compete with shipping features for the same sprint. If your version is past EOL and migration isn’t happening this quarter, TuxCare’s Endless Lifecycle Support for Laravel keeps it patched until you’re ready to move.



