laravel-security
Skill0 starsChanged 43 days agoApache-2.0View on GitHub
Security review and secure-coding guidance for Laravel (10.x–13.x) applications. Use when auditing, reviewing, pentest-prepping or hardening a Laravel/PHP codebase, or when writing or changing Laravel authentication, authorization (policies, gates), sessions, CSRF, Sanctum/Passport APIs, Eloquent queries, form requests and validation, Blade views, file uploads, Livewire/Inertia/Filament code, or configuration (.env, config/*.php, bootstrap/app.php). Triggers on projects containing artisan and laravel/framework in composer.json. Covers authentication, authorization/IDOR, sessions, CSRF, validation and mass assignment, SQL/command/deserialization injection, XSS, SSRF, uploads, secrets and debug exposure, API security, dependencies, and fix verification.
- Reads credentials
19 other projects use a version of this.
Adopted by
Most recently taken up by
- Rojios/everything-claude-codetheir own version, first seen Nov 8, 2026
- renepf/claude-skillstheir own version, first seen Oct 25, 2026
- davidanthonyn/claude-skillstheir own version, first seen Oct 20, 2026
- STIFLEUR390/agent-skillstheir own version, first seen Oct 16, 2026
- ashrafiucse/security-audit-skillstheir own version, first seen Oct 13, 2026
- gongyijie85/dsh-ecctheir own version, first seen Oct 13, 2026
Used with
Projects with it also use skill-creator.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
SentrySkillskill-creator
Create new agent skills following the Agent Skills specification. Use when asked to "create a skill", "add a new skill", "write a skill", "make a skill", "build a skill", or scaffold a new skill with SKILL.md. Guides through requirements, writing, registration, and verification.
Also in 4 projects that use this
Other versions19 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 19
- loulanyue/awesome-claude-notes★ 272same name
- Fmarzochi/EGC★ 61same name
- pekral/cursor-rules★ 10same name
- gongyijie85/dsh-ecc★ 8same name
- PUPT-Quantum-Leap/capstone-nlcom-volunteer-management-system★ 4same name
- ashrafiucse/security-audit-skills★ 2same name
- ronmkr/PromptBook★ 2same name
- wakqasahmed/skills★ 2same name
- junimnjw/everything-claude-code★ 1same name
- NISTALTALSON/essential-claude-skills★ 1same name
9 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository11 other files it gives its agents.
- appsec-reviewSkill
- aspnet-core-securitySkill
- django-securitySkill
- flutter-securitySkill
- nestjs-securitySkill
- nextjs-securitySkill
- nodejs-securitySkill
- react-securitySkill
- spring-boot-securitySkill
- supabase-securitySkill
- wordpress-securitySkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
20 rules. 0 are common, 5 are distinctive, and 15 are very specific to this project.
0
Common
Most of the 13 edited versions we read keep these.
5
Distinctive
This author's own choices. Worth a look.
15
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Very specific: line 18
All paths in this skill (references/...) are relative to the directory containing this SKILL.md.
- Very specific: line 25
If the appsec-review skill is installed, it owns the overall methodology and report format.
- Very specific: line 32
Read the installed version from composer.lock ("name": "laravel/framework" → "version"), not the constraint in composer.json.
- Very specific: line 37
Check support status. Laravel 10 (security fixes ended Feb 2025) and 11 (ended Mar 2026) no longer receive security patches.
- Distinctive: line 67
Every finding needs evidence: file, line, the code or config, and the path from attacker-controlled input to the sensitive operation.
- Distinctive: line 109
Do not report these without further evidence:
- Very specific: line 114
FormRequest::authorize()returningtrue: when authorization is enforced by a policy, can middleware or Gate::authorize() elsewhere on … - Very specific: line 119
$fillablecontainingpassword: when the value is hashed via the hashed cast or a mutator and only the user's own record is updated. - Distinctive: line 132
When writing Laravel code, default to:
- Very specific: line 135
Authorize every action server-side: Use policies plus Gate::authorize(), can: middleware or ->can() on routes.
- Very specific: line 136
Models declare
$fillable: Never put role, is_admin, team_id, user_id, email_verified_at, balances or status fields in $fillable if … - Distinctive: line 137
Use Eloquent and the query builder with bindings: Never interpolate variables into raw SQL.
- Very specific: line 138
Escape output with
{{ }}: Use @json/Js::from for data in <script>. - Very specific: line 140
Secrets: read through config(), never env() outside config/*.php.
- Very specific: line 141
Sessions & auth: use the starter kit / Fortify flows.
- Very specific: line 143
Never: use unserialize, decrypt() (it unserializes by default.
- Very specific: line 145
Add a feature test for the security boundary you just wrote (e.g.
- Very specific: line 149
Use these when appsec-review is not installed.
- Distinctive: line 155
Hardening: not demonstrably exploitable, but a stronger control is recommended.
- Very specific: line 173
Rules: never invent files, routes, packages or config values.
“Common” is measured: kept by at least 6 in 10 of the 13 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Laravel Security
Find, explain, fix and verify security issues in Laravel applications, and write new Laravel code that does not introduce them.
All paths in this skill (references/...) are relative to the directory containing this SKILL.md.
Modes
- Review mode: the user asks for an audit, security review, pentest prep, or "is this secure?". Follow the workflow below and produce findings.
- Build mode: you are writing or modifying Laravel code. Apply the build-mode guardrails to every change. Load only the reference for the area you are touching.
If the appsec-review skill is installed, it owns the overall methodology and report format. This skill supplies the Laravel-specific knowledge. If it is not installed, use the evidence and reporting rules below.
Review workflow
1. Confirm the stack and version
- Confirm Laravel:
artisanat the project root andlaravel/frameworkincomposer.json. - Read the installed version from
composer.lock("name": "laravel/framework"→"version"), not the constraint incomposer.json. - Determine the application structure era. Upgraded apps often keep the old structure, so check the files rather than assuming from the version:
- Laravel ≤10 structure:
app/Http/Kernel.php,app/Http/Middleware/*.php(VerifyCsrfToken,TrustProxies,TrustHosts,EncryptCookies),app/Exceptions/Handler.php. - Laravel 11+ structure: middleware, exceptions and routing configured in
bootstrap/app.phpvia->withMiddleware(...)and->withExceptions(...). There is noroutes/api.phpuntilphp artisan install:apiis run.
- Laravel ≤10 structure:
- Record security-relevant packages from
composer.lockandpackage.json: Sanctum, Passport, Fortify, Breeze, Jetstream, Socialite, Livewire, Inertia, Filament, Nova, Telescope, Horizon, Pulse, Debugbar, Ignition, spatie/laravel-permission, Cashier, Lighthouse, any JWT package. - Check support status. Laravel 10 (security fixes ended Feb 2025) and 11 (ended Mar 2026) no longer receive security patches. 12 is supported until Feb 2027 and 13 until Mar 2028. An unsupported framework version is a finding; see
references/dependencies.md.
2. Map the attack surface
- Routes:
routes/web.php,routes/api.php,routes/channels.php,routes/console.php, and any route files registered inbootstrap/app.phpor aRouteServiceProvider. If you can run commands,php artisan route:list -vshows each route's middleware. - Entry points beyond controllers: Livewire components (
app/Livewire,app/Http/Livewire), Filament resources and panels, Nova resources, broadcast channels, queued jobs and listeners, console commands and scheduled tasks, webhook controllers, GraphQL schema. - Unauthenticated routes, admin routes, file download/upload routes, and anything taking an ID, URL, path, filename, sort column or HTML.
3. Review each area
Load the reference for each area as you reach it. Do not load all of them up front.
| Area | Reference | Start by looking for |
| Authentication | references/authentication.md | Custom login code, password hashing, reset flows, throttling, Auth::loginUsingId, TrustHosts |
| Authorization / IDOR | references/authorization.md | Route model binding without policies, find($request->id), Livewire actions, Filament/Nova access, Gate::before |
| Sessions | references/sessions.md | config/session.php, logout code, custom session-based auth, serialization, remember-me |
| CSRF | references/csrf.md | CSRF except lists, state-changing GET routes, allowSameSite, Sanctum stateful domains |
| Validation & mass assignment | references/validation-mass-assignment.md | $request->all(), $guarded = [], Model::unguard(), forceFill, array rules without keys |
| Injection & deserialization | references/injection.md | DB::raw, *Raw( with interpolation, dynamic column names, Process/exec, unserialize, decrypt(, Blade::render |
| XSS & output | references/xss.md | {!! !!}, href="{{ ... }}", Str::markdown, raw response() HTML, SVG uploads |
| SSRF & redirects | references/ssrf-redirects.md | Http::get($userUrl), file_get_contents, PDF renderers, redirect()->to($request->...) |
| File uploads & storage | references/file-uploads.md | storeAs(..., getClientOriginalName()), public disk, response()->download($path), mimes vs extension |
| Secrets & configuration | references/secrets-config.md | .env in VCS, APP_DEBUG, APP_KEY, env() outside config/, debug tools, CORS, trustProxies |
| API security | references/api-security.md | Sanctum/Passport config, returning models directly, paginate($request->per_page), webhooks, broadcast channels, rate limits |
| Dependencies | references/dependencies.md | composer audit, composer.lock, dev packages in require, EOL versions |
| Verification | references/verification.md | How to prove each finding and each fix |
4. Classify and report
Every finding needs evidence: file, line, the code or config, and the path from attacker-controlled input to the sensitive operation. Classify findings using the rules below.
5. Remediate and verify (when asked to fix)
Explain the issue before changing code. Make the smallest change that uses Laravel's own mechanism. Then verify using references/verification.md: the attack no longer works, legitimate use still works, and the same pattern isn't repeated elsewhere (search for siblings).
High-signal patterns
These are investigation signals, not findings. Trace each one before reporting.
# Injection / RCE
DB::raw( ->whereRaw( ->selectRaw( ->orderByRaw( ->havingRaw( DB::statement( DB::unprepared( DB::select("...$
->orderBy($request ->where($request->input('field') DB::table($request
exec( shell_exec( system( passthru( proc_open( popen( `backticks` Process::run("...$
unserialize( decrypt($request Crypt::decrypt( Blade::render( Blade::compileString( eval(
'serialization' => 'php' 'serializable_classes' => true
# Authorization
Route::get('{model}') + controller without authorize/policy ::find($request-> ::findOrFail($id)
public function authorize() { return true; } Gate::before( withoutGlobalScopes( canAccessPanel viewNova
Broadcast::channel(... => true)
# Mass assignment
$guarded = [] Model::unguard( ->forceFill($request ::create($request->all()) ->update($request->all())
# XSS
{!! Str::markdown( ->toHtml() new HtmlString( href="{{ response($request v-html dangerouslySetInnerHTML
# Files
getClientOriginalName() getClientOriginalExtension() storeAs( ->download( Storage::disk('public') 'image:allow_svg'
# Config / secrets
APP_DEBUG=true APP_ENV=local env( (outside config/) 'supports_credentials' => true trustProxies(at: '*') $proxies = '*'
Telescope Debugbar Horizon Pulse log-viewer phpinfo(
# SSRF / redirect
Http::get($ file_get_contents($ fopen($url redirect()->to($request redirect($request away($request isRemoteEnabledCommon false positives
Do not report these without further evidence:
- No
session()->regenerate()afterAuth::attempt()/Auth::login(). Laravel'sSessionGuardregenerates the session ID itself during login (updateSession()→$this->session->regenerate(true)), so this alone is not session fixation. It is a finding only for custom auth that writes user identity into the session manually. DB::raw/whereRawwith?placeholders or named bindings and only constant SQL text.{!! !!}on output from a trusted sanitizer (HTMLPurifier, symfony/html-sanitizer) or framework helpers like$errors/csrf_field(). Confirm the sanitizer config before clearing it.FormRequest::authorize()returningtruewhen authorization is enforced by a policy,canmiddleware orGate::authorize()elsewhere on the same route.orderBy($column)with an allow-listed column (in_array,Rule::in, an enum, or spatie/laravel-query-builderallowedSorts).- Paths passed to Flysystem (
Storage::...) containing user input. Flysystem rejects..traversal outside the disk root, so treat it as a disk-scoped access-control question, not arbitrary file read. Native PHP paths (response()->download(storage_path(...)),file_get_contents) have no such protection. APP_DEBUG=truein.env.exampleor a local-only.envthat is not deployed.- CSRF exclusions for webhook routes that verify a provider signature.
$fillablecontainingpasswordwhen the value is hashed via thehashedcast or a mutator and only the user's own record is updated.- Sanctum
expiration => nullis a hardening point, not a vulnerability, unless tokens are long-lived in a high-risk context or there is no revocation path.
Severity calibration
Common under-ratings to avoid:
- User-controlled
orderBy/wherecolumn names reaching a table with secret columns (password,remember_token,two_factor_secret, API tokens): the sort order is an oracle that leaks secret values character by character. Rate it High (Medium if no secret columns exist), not Low "input validation". - Livewire action IDOR (unlocked ID property plus an action without authorization) has the same impact as a controller IDOR. Rate it by the data and action exposed (usually High).
- Mass assignment of a privilege field + an admin area that trusts it is a chain. Rate the chain (often Critical), and fix both parts.
Build-mode guardrails
When writing Laravel code, default to:
- Validate with Form Requests and only use
$request->validated()/$request->safe()->only([...]). Never$request->all()intocreate,update,fillor a query. - Authorize every action server-side. Use policies plus
Gate::authorize(),can:middleware or->can()on routes. Scope queries through the owner ($request->user()->posts()->findOrFail($id)).@canin Blade only hides UI. - Models declare
$fillable. Never putrole,is_admin,team_id,user_id,email_verified_at, balances or status fields in$fillableif they're set from user input. Set those explicitly. - Use Eloquent and the query builder with bindings. Never interpolate variables into raw SQL. Allow-list dynamic column names and sort directions.
- Escape output with
{{ }}. Use@json/Js::fromfor data in<script>. Validate URLs before putting them inhref/src(http/httpsonly). Sanitize any HTML you must render raw. - Uploads: validate with
File::types([...])->max(...)ormimes:, store withstore()(random name), keep private files off thepublicdisk, and serve them through an authorized controller orStorage::temporaryUrl(). - Secrets: read through
config(), neverenv()outsideconfig/*.php. Never commit.envor keys, and never put real defaults inenv('KEY', 'real-secret'). - Sessions & auth: use the starter kit / Fortify flows. On logout call
Auth::logout(),$request->session()->invalidate()and$request->session()->regenerateToken(). Throttle login, OTP and reset endpoints withRateLimiter/throttle:. - Outbound requests to user-supplied URLs go through an allow-list. Redirects to user-supplied targets use
redirect()->intended()or a relative-path check. - Never use
unserialize,decrypt()(it unserializes by default; usedecryptString),evalorBlade::renderon user input. Never shell out with interpolated strings; passProcess::run([...])an argument array. - APIs return API Resources with explicit fields, not raw models. Clamp
per_page. Enforce token abilities or scopes. - Add a feature test for the security boundary you just wrote (e.g. another user gets
403,is_adminis ignored, a.phpupload gets422).
Evidence and reporting rules
Use these when appsec-review is not installed.
Classification
- Confirmed: the code/config path from attacker input to impact is fully traced, or it was safely demonstrated.
- Likely: strong evidence, but one runtime condition (deployment config, a middleware applied elsewhere, environment values) could not be verified. State which condition.
- Hardening: not demonstrably exploitable, but a stronger control is recommended.
- Informational: context with no direct security impact.
Severity follows impact × exploitability × required privileges × exposure. Do not raise severity because a scary keyword appears. Unauthenticated RCE, auth bypass and cross-tenant data access are Critical/High. Issues needing an admin account or an unusual configuration go down.
Finding format
### [SEVERITY] Title — Confirmed|Likely|Hardening
- **Location:** `path/to/File.php:42` (`Class::method`)
- **Evidence:** the exact code/config, and how attacker input reaches it
- **Impact:** what an attacker gains, and who the attacker must be
- **Preconditions:** auth level, configuration, deployment assumptions
- **Fix:** smallest Laravel-native change (code snippet)
- **Verify:** test or request that proves the fix
- **Refs:** CWE / OWASP / Laravel docs linkRules: never invent files, routes, packages or config values. Redact secrets (show APP_KEY=base64:****). Say explicitly when runtime verification was not performed. Only test applications the user is authorized to assess, and use non-destructive checks.
References
- Laravel docs (match the project's version): https://laravel.com/docs
- Laravel security advisories: https://github.com/laravel/framework/security/advisories
- OWASP Cheat Sheet Series (Laravel cheat sheet): https://cheatsheetseries.owasp.org/cheatsheets/Laravel_Cheat_Sheet.html
- OWASP ASVS 5.0: https://owasp.org/www-project-application-security-verification-standard/
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

