Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Laravel security best practices for authn/authz, validation, CSRF, mass assignment, file uploads, secrets, rate limiting, and secure deployment.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-14 | ✗→✓ | ▲ Improved | 14% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 70% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 107% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 94% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 43% | 0% |
Comprehensive security guidance for Laravel applications to protect against common vulnerabilities.
VerifyCsrfToken, security headers via SecurityHeaders).auth:sanctum, $this->authorize, policy middleware).UploadInvoiceRequest) before it reaches services.RateLimiter::for('login')) alongside auth controls.URL::temporarySignedRoute + signed middleware).APP_DEBUG=false in productionAPP_KEY must be set and rotated on compromiseSESSION_SECURE_COOKIE=true and SESSION_SAME_SITE=lax (or strict for sensitive apps)SESSION_HTTP_ONLY=true to prevent JavaScript accessSESSION_SAME_SITE=strict for high-risk flowsExample route protection:
phpuse Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::middleware('auth:sanctum')->get('/me', function (Request $request) { return $request->user(); });
Hash::make() and never store plaintextphpuse Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules\Password; $validated = $request->validate([ 'password' => ['required', 'string', Password::min(12)->letters()->mixedCase()->numbers()->symbols()], ]); $user->update(['password' => Hash::make($validated['password'])]);
php$this->authorize('update', $project);
Use policy middleware for route-level enforcement:
phpuse Illuminate\Support\Facades\Route; Route::put('/projects/{project}', [ProjectController::class, 'update']) ->middleware(['auth:sanctum', 'can:update,project']);
$fillable or $guarded and avoid Model::unguard()phpDB::select('select * from users where email = ?', [$email]);
{{ }}){!! !!} only for trusted, sanitized HTMLVerifyCsrfToken middleware enabled@csrf in forms and send XSRF tokens for SPA requestsFor SPA authentication with Sanctum, ensure stateful requests are configured:
php// config/sanctum.php 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),
phpfinal class UploadInvoiceRequest extends FormRequest { public function authorize(): bool { return (bool) $this->user()?->can('upload-invoice'); } public function rules(): array { return [ 'invoice' => ['required', 'file', 'mimes:pdf', 'max:5120'], ]; } }
php$path = $request->file('invoice')->store( 'invoices', config('filesystems.private_disk', 'local') // set this to a non-public disk );
throttle middleware on auth and write endpointsphpuse Illuminate\Cache\RateLimiting\Limit; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; RateLimiter::for('login', function (Request $request) { return [ Limit::perMinute(5)->by($request->ip()), Limit::perMinute(5)->by(strtolower((string) $request->input('email'))), ]; });
Use encrypted casts for sensitive columns at rest.
phpprotected $casts = [ 'api_token' => 'encrypted', ];
Example middleware to set headers:
phpuse Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; final class SecurityHeaders { public function handle(Request $request, \Closure $next): Response { $response = $next($request); $response->headers->add([ 'Content-Security-Policy' => "default-src 'self'", 'Strict-Transport-Security' => 'max-age=31536000', // add includeSubDomains/preload only when all subdomains are HTTPS 'X-Frame-Options' => 'DENY', 'X-Content-Type-Options' => 'nosniff', 'Referrer-Policy' => 'no-referrer', ]); return $response; } }
config/cors.phpphp// config/cors.php return [ 'paths' => ['api/*', 'sanctum/csrf-cookie'], 'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], 'allowed_origins' => ['https://app.example.com'], 'allowed_headers' => [ 'Content-Type', 'Authorization', 'X-Requested-With', 'X-XSRF-TOKEN', 'X-CSRF-TOKEN', ], 'supports_credentials' => true, ];
phpuse Illuminate\Support\Facades\Log; Log::info('User updated profile', [ 'user_id' => $user->id, 'email' => '[REDACTED]', 'token' => '[REDACTED]', ]);
composer audit regularlyUse signed routes for temporary, tamper-proof links.
phpuse Illuminate\Support\Facades\URL; $url = URL::temporarySignedRoute( 'downloads.invoice', now()->addMinutes(15), ['invoice' => $invoice->id] );
phpuse Illuminate\Support\Facades\Route; Route::get('/invoices/{invoice}/download', [InvoiceController::class, 'download']) ->name('downloads.invoice') ->middleware('signed');
Other measured skills in the registry, with their headline benchmark lift.