Engineering & Performance
PHP Security in 2026: OWASP Top 10 Applied to Real PHP Code
Every penetration test report I've reviewed in the last decade hits the same handful of issues. Here's the OWASP Top 10 translated into PHP and Laravel patterns I actually find in client codebases.
1. Broken access control
The #1 issue in real codebases: routes that check authentication but not authorization.
// Bad: any logged-in user can edit any invoice
Route::put('/invoices/{invoice}', [InvoiceController::class, 'update'])
->middleware('auth');
// Good: scoped + policy
Route::put('/invoices/{invoice}', [InvoiceController::class, 'update'])
->middleware(['auth', 'can:update,invoice']);2. Cryptographic failures
- Never roll your own crypto. Use Laravel's `Crypt`, `Hash::make`, or libsodium.
- Never store passwords with `md5()` or `sha1()` — use `password_hash($pw, PASSWORD_BCRYPT)`.
- TLS everywhere — HSTS header, no mixed content.
3. Injection
SQL injection in 2026 is almost always raw queries built with string concatenation:
// Bad
$user = DB::select("SELECT * FROM users WHERE email = '$email'");
// Good — bound parameters
$user = DB::select('SELECT * FROM users WHERE email = ?', [$email]);
// Better — Eloquent
$user = User::where('email', $email)->first();4. Insecure design
Things like 'password reset by email only' (account takeover via email compromise), or 'admin users decided by email domain' (anyone with a @company.com email is admin). These aren't bugs — they're design flaws. Threat-model before building.
5. Security misconfiguration
- `APP_DEBUG=true` in production: leaks stack traces, env vars, DB credentials.
- Default Laravel `APP_KEY` from a tutorial — anyone can decrypt your sessions.
- Permissive CORS (`Access-Control-Allow-Origin: *`) on authenticated endpoints.
- Open S3 buckets / publicly readable storage disks.
6. Vulnerable & outdated components
Run `composer audit` in CI. Subscribe to security advisories for Laravel, Symfony, and your top dependencies. Out-of-date components account for a huge share of breaches.
7. Identification & authentication failures
- Rate-limit login attempts (Laravel's `RateLimiter` facade).
- Force re-auth before sensitive operations (password change, email change).
- Invalidate all sessions on password reset.
- Add 2FA for admins — at minimum.
8. Software & data integrity failures
- Verify webhook signatures (Stripe, GitHub, etc.) before processing.
- Pin composer dependencies and review `composer.lock` changes.
- Sign releases / artifacts in CI.
9. Security logging & monitoring failures
If you don't log it, it didn't happen. Log auth events, permission changes, admin actions, failed logins. Forward to a SIEM or at least Sentry for alerting on anomalies.
10. Server-side request forgery (SSRF)
Any feature that fetches a URL the user provides (image proxy, webhook tester, RSS reader) is an SSRF candidate. Block requests to internal IP ranges (10.x, 169.254.x, localhost). Use allowlists, not blocklists.
Quick wins to ship today
- Run `composer audit` and fix everything high/critical.
- Set `APP_DEBUG=false` in every non-local env.
- Add rate limiting on /login and /password/reset.
- Audit every route — does it have an authorization check, not just authentication?
- Enable HSTS and a strict Content-Security-Policy header.
Need a hand?
Hiring or modernizing PHP? Let's talk.
16+ years building, scaling, and rescuing PHP applications. Direct contact, no marketplace, US time zones from LATAM.
Related reading
Scaling a Laravel Application: From 100 to 100k Users
Concrete techniques for scaling Laravel — caching, queues, database tuning, and horizontal scaling — through every order of magnitude of growth.
Designing REST APIs in PHP/Laravel That Don't Become Legacy
Patterns for designing PHP and Laravel REST APIs that age well — versioning, OpenAPI, idempotency, error contracts, and backward compatibility.