Engineering & Performance

PHP Security in 2026: OWASP Top 10 Applied to Real PHP Code

·11 min read·By Abimael Espinoza

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

  1. Run `composer audit` and fix everything high/critical.
  2. Set `APP_DEBUG=false` in every non-local env.
  3. Add rate limiting on /login and /password/reset.
  4. Audit every route — does it have an authorization check, not just authentication?
  5. 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