Claude
Skills
Sign in
Back

laravel-owasp-security

Included with Lifetime
$97 forever

OWASP Top 10 security audit and secure coding guidelines for Laravel + React/Inertia.js applications. Use when auditing for vulnerabilities ("run OWASP audit", "security review", "check my app security") or writing secure Laravel code involving auth, payments, file uploads, or API design. Triggers on security-related tasks, payment handling, authentication, or any request to audit a Laravel codebase.

Design

What this skill does


# Laravel OWASP Security

Dual-purpose security skill for Laravel 13 + React/Inertia.js applications. Run a full OWASP Top 10 audit against a codebase, or use as a secure coding reference when building features.

## How to Audit

### Step 1: Detect Stack

Check if the project uses React + Inertia.js by looking for:
- `app/Http/Middleware/HandleInertiaRequests.php` exists
- `resources/js/` contains `.tsx` or `.jsx` files
- `inertiajs/inertia-laravel` in `composer.json`
- `@inertiajs/react` in `package.json`

**If detected**, state at the top of the report:
> "React + Inertia.js detected — Laravel OWASP checklist AND React/Inertia security checks will both be applied."

**If not detected**, state:
> "No React/Inertia.js detected — applying Laravel OWASP checklist only."

### Step 2: Determine Scope

- If arguments provided (`$ARGUMENTS`): review only those files or features
- If no arguments: review the entire codebase

### Step 3: Run Checklist

Work through every item below. For each, output:
- **PASS** — brief confirmation of what was verified
- **FAIL** — exact `file:line`, a description of the vulnerability (do NOT reproduce any code, values, API keys, tokens, or .env contents from the file), and a fix recommendation
- **N/A** — if the check does not apply to this project

---

## OWASP Top 10 Checklist

### 1. Broken Access Control (A01:2021)

- [ ] Middleware protects all route groups by role (`auth`, `role:admin`, etc.)
- [ ] Resource queries scoped to authenticated user — `->where('user_id', auth()->id())`
- [ ] No direct object reference without ownership check
- [ ] Gates and Policies used to authorize resource access
- [ ] Frontend role checks are mirrored server-side — never rely on React UI checks alone

### 2. Cryptographic Failures (A02:2021)

- [ ] Passwords hashed with `Hash::make()` or `'hashed'` Eloquent cast — never stored as plaintext
- [ ] No MD5 or SHA1 used for password hashing
- [ ] Sensitive fields (API keys, secrets) encrypted with `Crypt::encryptString()` or `'encrypted'` Eloquent cast
- [ ] `APP_KEY` is long, random, and unique per environment
- [ ] Signed URLs (`URL::signedRoute()`) used for sensitive one-time actions (password reset, email verify)

### 3. Injection (A03:2021)

**SQL & Mass Assignment:**
- [ ] No string concatenation in `whereRaw()`, `selectRaw()`, `orderByRaw()` — use `?` bindings
- [ ] Column names never derived from user input without a whitelist
- [ ] No `$request->all()` passed directly to `create()`, `fill()`, or `update()`
- [ ] No `forceFill()` or `forceCreate()` with unvalidated user input
- [ ] Models define `$fillable` explicitly — not `$guarded = []`
- [ ] Controllers use `$request->validated()` for mass operations

**XSS — Blade & React:**
- [ ] No `{!! $userInput !!}` in Blade templates with untrusted data
- [ ] `{{ }}` used for all user-supplied Blade output
- [ ] No `dangerouslySetInnerHTML` in React without `DOMPurify.sanitize()` first
- [ ] `href` and `src` attributes not set from unvalidated user input
- [ ] No `eval()`, `new Function()`, or `setTimeout(string)` with user-controlled strings
- [ ] External CDN scripts use Subresource Integrity (`integrity="sha384-..."`)

### 4. Insecure Design (A04:2021)

- [ ] Business logic enforced server-side — prices, totals, and discounts never trusted from client input
- [ ] Sensitive operations require secondary confirmation (e.g. password re-entry for account deletion)
- [ ] No mass action endpoints without per-item authorization check
- [ ] Admin-only features isolated behind separate middleware — not just hidden in the UI
- [ ] Payment amounts and enrollment states calculated server-side, not passed as form inputs

### 5. Security Misconfiguration (A05:2021)

- [ ] `APP_DEBUG=false` in production
- [ ] `.env` is in `.gitignore` and never committed
- [ ] Database uses a restricted user — not root/admin — in production
- [ ] `storage/` and `bootstrap/cache/` have correct permissions (not world-writable)
- [ ] `APP_KEY` is set and unique per environment
- [ ] CORS `allowed_origins` is not `['*']` for authenticated API routes

### 6. Vulnerable & Outdated Components (A06:2021)

- [ ] `composer audit` passes with no known CVEs
- [ ] `npm audit` passes with no known CVEs
- [ ] Laravel framework is on a supported version

### 7. Identification & Authentication Failures (A07:2021)

**Auth:**
- [ ] Using Laravel Breeze, Fortify, or Jetstream — not custom-rolled auth
- [ ] Passwords hashed with bcrypt or argon2 (Laravel default)
- [ ] Login route rate limited — `throttle` middleware or `RateLimiter` in `LoginRequest`
- [ ] Password reset and email verification routes rate limited
- [ ] Payment and sensitive action routes have appropriate rate limits
- [ ] `session()->regenerate()` called after successful login

**Cookie & Session:**
- [ ] `http_only = true` in `config/session.php`
- [ ] `same_site = lax` or `strict` in `config/session.php`
- [ ] `secure = true` or `null` (auto for HTTPS) in `config/session.php`
- [ ] `lifetime` is a reasonable value (15–30 min recommended for most apps)
- [ ] `domain = null` unless subdomains are needed
- [ ] `EncryptCookies` middleware is in the web group

### 8. Software & Data Integrity Failures (A08:2021)

**CSRF:**
- [ ] `VerifyCsrfToken` middleware active in the web group
- [ ] Only stateless routes (webhooks, external callbacks) are excluded from CSRF
- [ ] `@csrf` directive used in all non-Inertia POST forms
- [ ] Excluded routes in `validateCsrfTokens(except: [...])` are justified

**Deserialization:**
- [ ] No `unserialize($request->input(...))`
- [ ] No `eval($request->input(...))`
- [ ] No `extract($request->all())`

### 9. Security Logging & Monitoring Failures (A09:2021)

- [ ] Failed login attempts logged with IP and identifier
- [ ] Payment failures and exceptions logged
- [ ] Log entries do not contain raw passwords or secrets
- [ ] Monitoring in place (Laravel Telescope, Sentry, or similar)

### 10. Server-Side Request Forgery — SSRF (A10:2021)

- [ ] No `Http::get($request->input('url'))` with unvalidated URLs
- [ ] User-supplied URLs validated against an allowlist or scheme check
- [ ] Internal network addresses blocked from user-supplied URLs

---

## Additional Checks

> Not part of the OWASP Top 10 but critical for Laravel applications.

### Command Injection & Dangerous Functions

- [ ] No `exec()`, `shell_exec()`, `system()`, `passthru()` with user input
- [ ] No open redirects — no `redirect($request->input('url'))` with unvalidated URLs
- [ ] File uploads validate `mimes:`, `max:` — filenames never derived from raw user input

### Security Headers

- [ ] `Content-Security-Policy` set — with nonces (`Vite::useCspNonce()`) if possible
- [ ] `X-Frame-Options` set
- [ ] `X-Content-Type-Options` set
- [ ] `Strict-Transport-Security` set for HTTPS
- [ ] `Referrer-Policy` set
- [ ] `Permissions-Policy` set

---

## React + Inertia.js Checks

> Only run if React + Inertia.js detected in Step 1.

### R1. XSS in React Components

- [ ] No `dangerouslySetInnerHTML={{ __html: userInput }}` without `DOMPurify.sanitize()` first
- [ ] `href` and `src` attributes not set from unvalidated user input — `javascript:` URLs execute scripts
- [ ] No `eval()`, `new Function()`, or `setTimeout(string)` with user-controlled strings
- [ ] Links from user input validate scheme (`https://` or `http://` only)

### R2. Inertia.js Data Exposure (Critical)

- [ ] `HandleInertiaRequests::share()` does NOT expose passwords, tokens, or internal-only flags
- [ ] Controllers use `->only([...])` or API Resources — not raw model `toArray()`
- [ ] All Inertia props are treated as public — visible in `data-page` HTML attribute on initial load
- [ ] Payment secret keys and admin-only credentials are never passed as Inertia props
- [ ] Inertia v2 History Encryption enabled for pages with sensitive data

### R3. CSRF in Inertia.js

- [ ] Inertia `X-XSRF-TOKEN` header not disabled
- [ ] Custom `fetch` or `axios` calls include CSRF tok

Related in Design