Examples
Worked OWASP Top 10 examples: one realistic vulnerable snippet per category, paired with the fix and the secondary defence. Use them as a teaching kit during onboarding or to seed unit tests for your security controls.
Top 10 categories, one worked example each
EXAMPLE
# ============================================================
# A01 Broken Access Control
# ============================================================
# VULNERABLE — endpoint trusts the user-supplied id without checking ownership
# Route::get('/orders/{id}', fn($id) => Order::find($id));
#
# FIX — scope by the authenticated user
# Route::get('/orders/{id}', fn(Request $r, $id) =>
# $r->user()->orders()->findOrFail($id)
# );
# ============================================================
# A02 Cryptographic Failures
# ============================================================
# VULNERABLE — passwords compared with == and stored as SHA-256
# if (sha256($pw) === $row['password_hash']) { /* login */ }
#
# FIX — argon2id, plus constant-time verification
# if (password_verify($pw, $row['password_hash'])) { /* login */ }
# ============================================================
# A03 Injection
# ============================================================
# VULNERABLE — concatenated SQL
# $rows = $pdo->query("SELECT * FROM users WHERE email = '{$_GET['e']}'");
#
# FIX — parameterised
# $stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
# $stmt->execute([$_GET['e']]);
# $rows = $stmt->fetchAll();
# ============================================================
# A04 Insecure Design — missing abuse model
# ============================================================
# VULNERABLE — no rate limit on a money-touching endpoint
# Route::post('/transfer', TransferController::class);
#
# FIX — per-user limit + idempotency key + amount-bounded
# Route::post('/transfer', TransferController::class)->middleware('throttle:5,60', 'idempotency');
# ============================================================
# A05 Security Misconfiguration
# ============================================================
# VULNERABLE — debug mode on in production
# APP_DEBUG=true (in .env on prod)
#
# FIX — bake the right value into the deploy + alert if it flips
# APP_DEBUG=false (and CI fails the build if it sees APP_DEBUG=true in a prod config)
# ============================================================
# A06 Vulnerable & Outdated Components
# ============================================================
# VULNERABLE — no SBOM, no dependency updates for 18 months
#
# FIX — Dependabot/Renovate + weekly composer audit + ship the patch
# pipeline:
# composer audit
# npm audit --omit=dev
# trivy fs --severity HIGH,CRITICAL .
# ============================================================
# A07 Identification & Authentication Failures
# ============================================================
# VULNERABLE — no MFA, no session rotation after password change
#
# FIX — TOTP/WebAuthn + rotate the session on every auth event
# auth()->logoutOtherDevices($new_password);
# session()->regenerate();
# ============================================================
# A08 Software & Data Integrity Failures
# ============================================================
# VULNERABLE — pulling a script from a CDN with no integrity check
# <script src="https://cdn.example/lib.js"></script>
#
# FIX — Subresource Integrity (SRI) for every external script/style
# <script src="https://cdn.example/lib.js"
# integrity="sha384-<hash>"
# crossorigin="anonymous"></script>
# ============================================================
# A09 Security Logging & Monitoring Failures
# ============================================================
# VULNERABLE — auth failures not logged, no alert on spikes
#
# FIX — structured log + alert
# Log::channel('security')->warning('auth_failure', [
# 'user_id' => $id, 'ip' => request()->ip(), 'route' => 'login',
# ]);
# Alert: > 50 auth_failures/min from same IP -> page on-call
# ============================================================
# A10 Server-Side Request Forgery (SSRF)
# ============================================================
# VULNERABLE — server fetches a user-supplied URL with no validation
# $pdf = file_get_contents($_GET['url']);
#
# FIX — resolve, deny private ranges, allow only HTTPS to expected hosts
# function safe_fetch(string $url): string {
# $parts = parse_url($url);
# if (($parts['scheme'] ?? '') !== 'https') abort(400);
# $ip = gethostbyname($parts['host']);
# if (in_private_range($ip)) abort(400);
# return file_get_contents($url);
# }
# AND require IMDSv2 on EC2 so SSRF cannot read instance creds even if it lands.
Why it matters
Make every Top 10 category a unit test in your security suite. Then every regression that re-introduces "echo $_GET" or "shell_exec($input)" fails CI before it merges. The Top 10 is most useful as a checklist of tests to write — not as a checklist of slides to nod through.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Adopt the Top 10 as a checklist for every new service. // Track findings in the same backlog as functional bugs.Try it Yourself »
Discussion
Loading…