Remote OpenClaw
Menu
SkillsMCPPluginsFree guideDigestSubmit MCPSkillPluginMCPMCP, plugin, or skillAdvertise
Remote OpenClaw
SkillsMCPPluginsFree guideDigestSubmit MCPSkillPluginMCPMCP, plugin, or skillAdvertise

Featured

Reach 56,000+ AI builders from just $0.50 a click

You only pay for clicks, never impressions - and it's far cheaper than Google Ads, with no monthly lock-in.

Advertise from $0.50/click →
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you →
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free →
CLN.Work — Stop prompting, start hiring AI employees logoCLN.Work — Stop prompting, start hiring AI employees

Turn your Claude agents into a real team — onboard them, assign tasks, and manage them like staff.

Hire AI employees →
Deploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch OpenClaw on Hostinger in about 60 seconds and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Deploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch Hermes on Hostinger in about 60 seconds, connect Telegram & Discord, and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Reach 56,000+ AI builders from just $0.50 a click

You only pay for clicks, never impressions - and it's far cheaper than Google Ads, with no monthly lock-in.

Advertise from $0.50/click →
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you →
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free →
CLN.Work — Stop prompting, start hiring AI employees logoCLN.Work — Stop prompting, start hiring AI employees

Turn your Claude agents into a real team — onboard them, assign tasks, and manage them like staff.

Hire AI employees →
Deploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch OpenClaw on Hostinger in about 60 seconds and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Deploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch Hermes on Hostinger in about 60 seconds, connect Telegram & Discord, and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Reach 56,000+ AI builders from just $0.50 a click

You only pay for clicks, never impressions - and it's far cheaper than Google Ads, with no monthly lock-in.

Advertise from $0.50/click →
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you →
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free →
CLN.Work — Stop prompting, start hiring AI employees logoCLN.Work — Stop prompting, start hiring AI employees

Turn your Claude agents into a real team — onboard them, assign tasks, and manage them like staff.

Hire AI employees →
Deploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch OpenClaw on Hostinger in about 60 seconds and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Deploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch Hermes on Hostinger in about 60 seconds, connect Telegram & Discord, and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Skills/affaan-m/everything-claude-code/laravel-security
laravel-security logo

laravel-security

affaan-m/everything-claude-code
5K installs216K stars
Run it on Hostinger →up to 70% off + an extra 10% with code ZACAARON10Free API →

Installation

npx skills add https://github.com/affaan-m/everything-claude-code --skill laravel-security

Summary

Laravel security best practices — authentication, authorization, Eloquent safety, CSRF, XSS prevention, API security, and secure deployment configurations.

SKILL.md

Laravel Security Best Practices

Comprehensive security guidelines for Laravel applications to protect against common vulnerabilities.

When to Activate

  • Setting up Laravel authentication and authorization (Sanctum, Passport, Jetstream, Breeze)
  • Implementing user roles, permissions, and policies
  • Configuring production security settings and environment variables
  • Reviewing Laravel applications for security vulnerabilities
  • Deploying Laravel applications to production
  • Writing secure Eloquent queries and migrations

Production Configuration

Essential Production Settings

// config/app.php
'env' => env('APP_ENV', 'production'),
'debug' => (bool) env('APP_DEBUG', false), // CRITICAL: Never true in production
'key' => env('APP_KEY'), // Must be set: php artisan key:generate

// config/session.php
'secure' => env('SESSION_SECURE_COOKIE', true),
'http_only' => true,
'same_site' => 'lax',

// Verify APP_KEY is set at boot
// bootstrap/app.php or a service provider
if (empty(config('app.key'))) {
    throw new RuntimeException('APP_KEY is not set. Run: php artisan key:generate');
}

Environment File Security

# NEVER commit .env to version control
# .gitignore already includes .env by default

# Use .env.example with placeholders instead
DB_PASSWORD=
APP_KEY=
SANCTUM_TOKEN_PREFIX=

# Validate required variables at boot
// In AppServiceProvider::boot()
$requiredKeys = ['app.key', 'database.connections.mysql.database', 'database.connections.mysql.username'];
foreach ($requiredKeys as $key) {
    if (empty(config($key))) {
        throw new RuntimeException("Missing required config key: {$key}");
    }
}

HTTPS Enforcement

// AppServiceProvider::boot() or middleware
if (app()->environment('production')) {
    URL::forceScheme('https');
    request()->server->set('HTTPS', 'on');
}

// config/app.php for trusted proxies (load balancers)
// Use specific IP ranges — * trusts all, allowing X-Forwarded-* spoofing
// AWS: '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'
'trusted_proxies' => ['10.0.0.0/8', '172.16.0.0/12'],

// Force HTTPS in production via middleware
// app/Http/Middleware/ForceHttps.php
public function handle($request, Closure $next)
{
    if (!$request->secure() && app()->environment('production')) {
        return redirect()->secure($request->getRequestUri());
    }
    return $next($request);
}

Authentication

Sanctum (API Token Authentication)

// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
    env('APP_URL') ? ',' . parse_url(env('APP_URL'), PHP_URL_HOST) : ''
)));

'expiration' => 60 * 24, // Token expiration in minutes (null = never)
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),

// Issuing tokens with abilities
$token = $user->createToken('api-token', ['read', 'write'])->plainTextToken;

// Validate abilities on routes
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/orders', function () {
        // User must have 'read' ability
        abort_unless(Auth::user()->tokenCan('read'), 403);
        // ...
    })->middleware('abilities:read');

    Route::post('/orders', function () {
        // User must have 'write' ability
        abort_unless(Auth::user()->tokenCan('write'), 403);
        // ...
    })->middleware('abilities:write');
});

Password Security

// config/hashing.php
// Default is bcrypt. Argon2id is stronger.
'bcrypt' => [
    'rounds' => env('BCRYPT_ROUNDS', 12), // Increase for stronger hashing
],

'argon' => [
    'memory' => 65536,
    'threads' => 4,
    'time' => 4,
],

// Password validation in RegisterRequest
public function rules(): array
{
    return [
        'password' => [
            'required',
            'confirmed',
            Password::min(12)
                ->letters()
                ->mixedCase()
                ->numbers()
                ->symbols()
                ->uncompromised(), // Checks haveibeenpwned
        ],
    ];
}

// Rate limit login attempts
// App\Http\Controllers\Auth\AuthenticatedSessionController
protected function authenticated(Request $request, $user)
{
    if ($user->wasRecentlyLockedOut()) {
        // Notify user of suspicious login
        $user->notify(new SuspiciousLoginNotification($request->ip()));
    }
}

Session Management

// config/session.php
'driver' => env('SESSION_DRIVER', 'database'), // database/redis > file
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
'encrypt' => env('SESSION_ENCRYPT', false),

// Regenerate session on login
// App\Http\Controllers\Auth\AuthenticatedSessionController
public function store(LoginRequest $request): RedirectResponse
{
    $request->authenticate();
    $request->session()->regenerate(); // CRITICAL: prevents session fixation
    return redirect()->intended(RouteServiceProvider::HOME);
}

// Invalidate session on logout
public function destroy(Request $request): RedirectResponse
{
    Auth::guard('web')->logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();
    return redirect('/');
}

Authorization

Gates

// App\Providers\AuthServiceProvider
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post): bool {
        return $user->id === $post->user_id;
    });

    Gate::define('publish-post', function (User $user): bool {
        return $user->role === 'editor' || $user->role === 'admin';
    });

    // Using before() for super-admin override
    Gate::before(function (User $user, string $ability): ?bool {
        if ($user->role === 'super-admin') {
            return true; // Grants all abilities
        }
        return null; // Fall through to normal checks
    });
}

// Usage in controllers
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);
    // Or: $this->authorize('update-post', $post);
    // Or: abort_unless(Auth::user()->can('update-post', $post), 403);
    // ...
}

Policies

// App\Policies\PostPolicy
class PostPolicy
{
    use HandlesAuthorization;

    public function viewAny(?User $user): bool
    {
        return true; // Public listing
    }

    public function view(?User $user, Post $post): bool
    {
        return $post->is_published || ($user && $user->id === $post->user_id);
    }

    public function create(User $user): bool
    {
        return $user->hasVerifiedEmail(); // Must verify email first
    }

    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id && $post->created_at->diffInDays(now()) <= 30;
    }

    public function restore(User $user, Post $post): bool
    {
        return $user->role === 'admin';
    }

    public function forceDelete(User $user, Post $post): bool
    {
        return $user->role === 'super-admin';
    }
}

// Register in AuthServiceProvider
protected $policies = [
    Post::class => PostPolicy::class,
];

// Controller usage
public function show(Post $post): View
{
    $this->authorize('view', $post);
    return view('posts.show', compact('post'));
}

// Blade usage
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan

@cannot('update', $post)
    <span>You cannot edit this post</span>
@endcannot

Middleware Authorization

// Using middleware in routes
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

Route::get('/posts/create', [PostController::class, 'create'])
    ->middleware('can:create,App\Models\Post');

// Custom authorization middleware
// app/Http/Middleware/CheckRole.php
class CheckRole
{
    public function handle(Request $request, Closure $next, string $role): mixed
    {
        if (!$request->user() || $request->user()->role !== $role) {
            abort(403, 'Unauthorized. This area requires role: ' . $role);
        }
        return $next($request);
    }
}

// Register in Kernel
protected $routeMiddleware = [
    'role' => \App\Http\Middleware\CheckRole::class,
];

// Route usage
Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin', [AdminController::class, 'index']);
});

Eloquent Security

Mass Assignment Protection

// BAD: $guarded = [] allows ALL columns to be mass-assigned
// NEVER use $guarded = [] in production

// GOOD: Whitelist fillable attributes
final class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'phone',
        'avatar',
    ];
    // NEVER add 'role', 'is_admin', 'is_verified' here
}

// GOOD: Explicitly control which fields can be filled in requests
public function store(StoreUserRequest $request): RedirectResponse
{
    $user = User::create($request->safe()->only([
        'name', 'email', 'phone', 'avatar'
    ]));
    // $request->safe() uses validated data only
    // $request->only() is NOT safe on its own without validation rules
}

// BAD: Creating a user with request data directly
User::create($request->all()); // VULNERABLE to mass assignment!

// BETTER: Use DTOs for creation
$user = User::create($request->validated()); // Only validated fields

SQL Injection Prevention

// GOOD: Eloquent automatically parameterizes queries
User::where('email', $userInput)->first();
User::whereRaw('email = ?', [$userInput])->first();

// GOOD: Query Builder also parameterizes
DB::table('users')->where('email', $userInput)->first();
DB::select('SELECT * FROM users WHERE email = ?', [$userInput]);

// BAD: Raw string interpolation
DB::select("SELECT * FROM users WHERE email = '{$userInput}'"); // VULNERABLE!
User::whereRaw("email = '{$userInput}'")->first(); // VULNERABLE!

// BAD: whereRaw/orderByRaw with unescaped input
User::orderByRaw($userInput); // VULNERABLE!
User::groupByRaw($userInput); // VULNERABLE!

// BAD: DB::statement with concatenation
DB::statement("INSERT INTO users (email) VALUES ('{$userInput}')"); // VULNERABLE!

Attribute Casting

final class User extends Authenticatable
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean', // Cast to boolean prevents string injection
        'settings' => 'array', // Automatically json_encode/json_decode
        'metadata' => 'encrypted:array', // Laravel 11+ encrypted casting
        'password' => 'hashed', // Laravel 10+ auto-hashes on set
    ];
}

Model Security

final class User extends Authenticatable
{
    // Hide sensitive attributes from JSON/API responses
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_secret',
        'two_factor_recovery_codes',
    ];

    // Append only safe computed attributes
    protected $appends = ['full_name']; // safe
    // NEVER append sensitive computed data
}

final class Post extends Model
{
    // Global scope to filter soft deleted records
    use SoftDeletes;

    // Prevent N+1 by restricting lazy loading (optional strict mode)
    // AppServiceProvider::boot()
    // Model::preventLazyLoading(!app()->isProduction());
}

CSRF Protection

Default Protection

// Laravel CSRF is enabled by default via VerifyCsrfToken middleware
// app/Http/Kernel.php (protected $middlewareGroups['web'])

// All POST/PUT/PATCH/DELETE forms must include @csrf
<form method="POST" action="/posts">
    @csrf
    <input type="text" name="title">
    <button type="submit">Create</button>
</form>

Excluding Routes (Carefully)

// app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends Middleware
{
    // Only exclude routes that have external CSRF protection (webhooks, etc.)
    protected $except = [
        'stripe/*', // Stripe webhooks use their own signature verification
        // Avoid blanket 'api/*' — stateful Sanctum routes need CSRF.
        // Exclude only specific stateless webhook/endpoint routes.
    ];
}

CSRF with JavaScript

<meta name="csrf-token" content="{{ csrf_token() }}">

<script>
// Axios example (Laravel ships with Axios)
axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector(
    'meta[name="csrf-token"]'
).getAttribute('content');

// Fetch example
fetch('/posts', {
    method: 'POST',
    headers: {
        'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
});
</script>

XSS Prevention

Blade Templating Security

{{-- SAFE: Auto-escaped by Blade --}}
{{ $userInput }}

{{-- DANGEROUS: Raw output — NEVER use with user input --}}
{!! $userInput !!}

{{-- SAFE: Only use {!! !!} with trusted content you control --}}
{!! $trustedHtmlFromYourServer !!}

{{-- GOOD: Use specific escaping directives --}}
@js($data) {{-- JSON encode for JavaScript --}}
@json($data) {{-- JSON encode in templates --}}

{{-- BAD: Direct user input in raw HTML --}}
<div>{!! $user->bio !!}</div> {{-- VULNERABLE if user provides bio --}}

Safe HTML Handling

// When you must allow some HTML, use a whitelist approach
use HTMLPurifier; // Requires: composer require ezyang/htmlpurifier

public function sanitizeHtml(string $dirty): string
{
    $config = \HTMLPurifier_Config::createDefault();
    $config->set('HTML.Allowed', 'p,b,i,a[href],ul,ol,li,br');
    $config->set('URI.AllowedSchemes', ['http', 'https', 'mailto']);
    $purifier = new \HTMLPurifier($config);
    return $purifier->purify($dirty);
}

// In blade:
<div>{!! $sanitizedContent !!}</div> {{-- Safe after purification --}}

JavaScript Context Escaping

{{-- SAFE: Blade @js escapes for JavaScript context --}}
<script>
    const user = @js($user); // JSON + escaped for JS context
    const settings = @json($settings); // Direct JSON encode
</script>

{{-- DANGEROUS: Manual JSON in JS context --}}
<script>
    const user = {{ json_encode($user) }}; // NOT escaped for JS!
</script>

HTTP Headers for XSS Protection

// App\Http\Middleware\SecurityHeaders.php
class SecurityHeaders
{
    public function handle(Request $request, Closure $next): mixed
    {
        $response = $next($request);

        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-Frame-Options', 'DENY');
        $response->headers->set('X-XSS-Protection', '1; mode=block');
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
        $response->headers->set(
            'Content-Security-Policy',
            "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'"
        );

        return $response;
    }
}

// Register in kernel
protected $middleware = [
    \App\Http\Middleware\SecurityHeaders::class,
];

Input Validation

Form Request Validation

final class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()?->can('create', Post::class) ?? false;
    }

    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:255', 'sanitize_html'],
            'content' => ['required', 'string', 'max:10000'],
            'image' => [
                'required',
                'image',
                'mimes:jpg,jpeg,png,gif,webp', // Whitelist specific types
                'max:2048', // 2MB max
            ],
            'tags' => ['array'],
            'tags.*' => ['integer', 'exists:tags,id'],
        ];
    }

    public function messages(): array
    {
        return [
            'title.max' => 'Post title must not exceed 255 characters.',
            'image.max' => 'Image must be under 2MB.',
        ];
    }

    // Sanitize input after validation
    public function validated($key = null, $default = null): mixed
    {
        $validated = parent::validated();
        $validated['title'] = strip_tags($validated['title']);
        return $key ? ($validated[$key] ?? $default) : $validated;
    }
}

Custom Validation Rules

// app/Rules/StrongPassword.php
class StrongPassword implements Rule
{
    public function passes($attribute, $value): bool
    {
        return preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#^()_\-+=])[A-Za-z\d@$!%*?&#^()_\-+=]{12,}$/', $value);
    }

    public function message(): string
    {
        return 'The :attribute must be at least 12 characters with uppercase, lowercase, number, and symbol.';
    }
}

// app/Rules/NotBlacklistedDomain.php
class NotBlacklistedDomain implements Rule
{
    private array $blacklisted = ['mailinator.com', 'guerrillamail.com'];

    public function passes($attribute, $value): bool
    {
        $domain = substr(strrchr($value, '@'), 1);
        return !in_array(strtolower($domain), $this->blacklisted);
    }

    public function message(): string
    {
        return 'Email from disposable domains is not allowed.';
    }
}

API Security

Rate Limiting

// App/Providers/RouteServiceProvider
protected function configureRateLimiting(): void
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });

    RateLimiter::for('auth', function (Request $request) {
        return Limit::perMinute(5)->by($request->ip())
            ->response(function () {
                return response()->json([
                    'message' => 'Too many login attempts. Try again in 1 minute.',
                ], 429);
            });
    });

    RateLimiter::for('uploads', function (Request $request) {
        return Limit::perHour(10)->by($request->user()?->id ?? $request->ip())
            ->response(function () {
                return response()->json([
                    'message' => 'Upload limit reached. Try again later.',
                ], 429);
            });
    });
}

// Route usage
Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
    Route::apiResource('posts', PostController::class);
});

Route::post('/login', [AuthController::class, 'login'])
    ->middleware('throttle:auth');

API Authentication — Sanctum vs Passport

// Sanctum (recommended for most apps — simple, first-party, SPA)
// config/sanctum.php
'expiration' => 60 * 24, // Tokens expire after 24 hours
'model' => User::class,

// Issuing scoped tokens
$token = $user->createToken('client-name', [
    'posts:read',
    'posts:write',
])->plainTextToken;

// Middleware scoping
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/posts', [PostController::class, 'index'])
        ->middleware('abilities:posts:read');

    Route::post('/posts', [PostController::class, 'store'])
        ->middleware('abilities:posts:write');
});

// Passport (OAuth2 — for third-party clients or complex auth flows)
// Install: composer require laravel/passport
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));

CORS Configuration

// config/cors.php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS', '')), // Whitelist specific origins
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => ['X-Total-Count', 'X-Pagination-Page'],
    'max_age' => 0,
    'supports_credentials' => true, // Required for Sanctum SPA auth
];

// NEVER: Allow all origins in production unless absolutely necessary
// 'allowed_origins' => ['*'], // Only for truly public APIs

File Upload Security

Validation

public function rules(): array
{
    return [
        'document' => [
            'required',
            'file',
            'mimes:pdf,doc,docx,xls,xlsx', // Whitelist specific MIME types
            'max:10240', // 10MB
            'extensions:pdf,doc,docx,xls,xlsx', // Verify extension matches MIME
        ],
        'avatar' => [
            'nullable',
            'image', // Ensures it's a valid image
            'mimes:jpg,jpeg,png,webp',
            'max:2048',
            'dimensions:min_width=100,min_height=100,max_width=2000,max_height=2000',
        ],
    ];
}

Secure Storage

// Store files outside public directory
$path = $request->file('document')->store('documents', 'local');
// Never use 'public' disk for sensitive documents

// Use signed URLs for temporary file access
use Illuminate\Support\Facades\Storage;

public function download(Request $request, string $path)
{
    // Generate temporary signed URL (expires in 15 minutes)
    $url = Storage::temporaryUrl($path, now()->addMinutes(15));

    // Validate user has permission
    $this->authorize('download', $path);

    return redirect($url);
}

// Storage configuration for cloud with encryption
// config/filesystems.php
's3' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION'),
    'bucket' => env('AWS_BUCKET'),
    'url' => env('AWS_URL'),
    'endpoint' => env('AWS_ENDPOINT'),
    'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
    'throw' => false,
    'server_side_encryption' => 'AES256', // Encrypt at rest
],

Dependencies and Secrets

Composer Security

# Always audit dependencies in CI
composer audit

# Pin major versions in composer.json
"laravel/framework": "^11.0",
"spatie/laravel-permission": "^6.0"

# Check for abandoned packages
composer why-not

# Keep lock file in version control (it pins exact versions)
# Run `composer update` deliberately, never in CI/CD

Secret Management

# .env file (NEVER commit)
# .gitignore includes .env by default

APP_KEY=base64:abc123...
DB_PASSWORD=secure_password
STRIPE_KEY=sk_live_...
SANCTUM_TOKEN_PREFIX=myapp_

# For production: Use a secret manager
# Deploy with: env $(aws secretsmanager get-secret-value --secret-id prod/db | jq ...) php artisan serve

# Validate secrets at boot (AppServiceProvider::boot)
$secrets = ['services.stripe.key', 'services.stripe.webhook_secret'];
foreach ($secrets as $key) {
    if (empty(config($key))) {
        Log::critical("Missing secret: {$key}");
    }
}

Queue Security

// Define a named rate limiter (typically in AppServiceProvider::boot())
RateLimiter::for('payments', fn () => Limit::perMinute(5));
// Encrypt sensitive job data by implementing the interface
final class ProcessPaymentJob implements ShouldQueue, ShouldBeEncrypted
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        private readonly string $paymentIntentId, // Public IDs are fine
        private readonly string $cardFingerprint, // Encrypted via ShouldBeEncrypted
    ) {}

    public function handle(): void
    {
        // Process payment
    }

    // Limit retries and delay between attempts
    public function retryUntil(): Carbon
    {
        return now()->addMinutes(5);
    }

    // Rate limit how many jobs of this type can run
    public function middleware(): array
    {
        return [
            new RateLimited('payments'),
        ];
    }
}

Logging Security Events

// config/logging.php
'channels' => [
    'security' => [
        'driver' => 'single',
        'path' => storage_path('logs/security.log'),
        'level' => 'warning',
    ],
],

// Audit log helper
final class SecurityLogger
{
    public static function log(string $event, array $context = []): void
    {
        Log::channel('security')->warning($event, array_merge([
            'user_id' => Auth::id(),
            'ip' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'url' => request()->fullUrl(),
            'timestamp' => now()->toIso8601String(),
        ], $context));
    }
}

// Usage
SecurityLogger::log('failed_login_attempt', ['email' => $email]);
SecurityLogger::log('password_change');
SecurityLogger::log('role_change', ['target_user' => $targetId, 'new_role' => 'admin']);
SecurityLogger::log('suspicious_activity', ['reason' => 'multiple_attempts_from_different_ips']);

Quick Security Checklist

CheckDescription
APP_DEBUG=falseNever run with debug enabled in production
APP_KEY setAlways run php artisan key:generate
HTTPS enforcedForce HTTPS in production via middleware or proxy
$fillable whitelistedNever use $guarded = []
CSRF active@csrf on all state-changing forms
Sanctum/Passport configuredAPI authentication with token abilities/scopes
Rate limiting appliedThrottle API and auth endpoints
Input validationFormRequest with specific rules, never $request->all()
File upload restrictionsValidate MIME types, size, dimensions
composer audit in CICheck dependencies for known vulnerabilities
password_hash / password_verifyUse Laravel's built-in hashing (bcrypt/Argon2)
Session regeneration on loginCall $request->session()->regenerate()
Security headers middlewareCSP, X-Frame-Options, X-Content-Type-Options
Logged security eventsAudit log for auth failures, role changes, suspicious activity
.env not committedVerify .gitignore includes .env

Related Skills

  • laravel-patterns — Laravel architecture, routing, Eloquent, and API patterns
  • backend-patterns — General backend API and database patterns
  • laravel-tdd — Laravel testing with PHPUnit and Pest

Score

0–100
71/ 100

Grade

B

Popularity23/30

4,982 installs — solid traction. Source repo has 215,671 GitHub stars.

Completeness27/30

Documented: full SKILL.md body, description, one-line install. Missing: category/license metadata.

Trust15/25

Community skill with a public GitHub source repository you can review.

Freshness6/15

No update timestamp is tracked for this skill in our catalog.

Scored automatically from popularity, completeness, trust, and freshness — computed only from data in our catalog, never fabricated.

Proud of your score? Add this badge to your README.

Paste a snippet into your GitHub README. The badge updates automatically and links back to this page.

Laravel Security skill score badge previewScore badge

Markdown

[![Laravel Security skill](https://www.remoteopenclaw.com/skills/affaan-m/everything-claude-code/laravel-security/badges/score.svg)](https://www.remoteopenclaw.com/skills/affaan-m/everything-claude-code/laravel-security)

HTML

<a href="https://www.remoteopenclaw.com/skills/affaan-m/everything-claude-code/laravel-security"><img src="https://www.remoteopenclaw.com/skills/affaan-m/everything-claude-code/laravel-security/badges/score.svg" alt="Laravel Security skill"/></a>

Laravel Security FAQ

How do I install the Laravel Security skill?

Run “npx skills add https://github.com/affaan-m/everything-claude-code --skill laravel-security” in your terminal. The skill is added to your agent's skills directory and picked up automatically on the next run — no restart or extra configuration needed.

What does the Laravel Security skill do?

Laravel security best practices — authentication, authorization, Eloquent safety, CSRF, XSS prevention, API security, and secure deployment configurations. The full SKILL.md on this page shows the exact instructions the skill gives your agent.

Is the Laravel Security skill free?

Yes. Laravel Security is a free, open-source skill published from affaan-m/everything-claude-code. As with any third-party skill, review the source repository before installing it into an agent with sensitive access.

Does Laravel Security work with Claude Code and OpenClaw?

Yes. Skills use the portable SKILL.md format, so Laravel Security works with Claude Code, OpenClaw, Codex, Hermes, and any other agent that reads SKILL.md skills.

Featured

Reach 56,000+ AI builders from just $0.50 a click

You only pay for clicks, never impressions - and it's far cheaper than Google Ads, with no monthly lock-in.

Advertise from $0.50/click →
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you →
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free →
CLN.Work — Stop prompting, start hiring AI employees logoCLN.Work — Stop prompting, start hiring AI employees

Turn your Claude agents into a real team — onboard them, assign tasks, and manage them like staff.

Hire AI employees →
Deploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch OpenClaw on Hostinger in about 60 seconds and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Deploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch Hermes on Hostinger in about 60 seconds, connect Telegram & Discord, and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Reach 56,000+ AI builders from just $0.50 a click

You only pay for clicks, never impressions - and it's far cheaper than Google Ads, with no monthly lock-in.

Advertise from $0.50/click →
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you →
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free →
CLN.Work — Stop prompting, start hiring AI employees logoCLN.Work — Stop prompting, start hiring AI employees

Turn your Claude agents into a real team — onboard them, assign tasks, and manage them like staff.

Hire AI employees →
Deploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch OpenClaw on Hostinger in about 60 seconds and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Deploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch Hermes on Hostinger in about 60 seconds, connect Telegram & Discord, and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Reach 56,000+ AI builders from just $0.50 a click

You only pay for clicks, never impressions - and it's far cheaper than Google Ads, with no monthly lock-in.

Advertise from $0.50/click →
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you →
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free →
CLN.Work — Stop prompting, start hiring AI employees logoCLN.Work — Stop prompting, start hiring AI employees

Turn your Claude agents into a real team — onboard them, assign tasks, and manage them like staff.

Hire AI employees →
Deploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your OpenClaw agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch OpenClaw on Hostinger in about 60 seconds and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
Deploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10 logoDeploy your Hermes agent in 60 seconds, live 24/7 — 10% off with code ZACAARON10

Launch Hermes on Hostinger in about 60 seconds, connect Telegram & Discord, and keep it live 24/7. Use code ZACAARON10 for 10% off.

Launch on Hostinger →
View on GitHub

Recommended skills

Browse all →
firebase-security-rules-auditor logo

firebase-security-rules-auditor

firebase/agent-skills

73K installsInstall
find-skills logo

find-skills

vercel-labs/skills

2.6M installsInstall
frontend-design logo

frontend-design

anthropics/skills

679K installsInstall
grill-me logo

grill-me

mattpocock/skills

593K installsInstall
vercel-react-best-practices logo

vercel-react-best-practices

vercel-labs/agent-skills

562K installsInstall
agent-browser logo

agent-browser

vercel-labs/agent-browser

558K installsInstall

Browse

Skills by category

Frontend250Git198Data154Testing120Design105Docs103Security96Automation87Backend76Devops37Productivity29Mcp23

Related guides

Hand-picked reading to help you choose, install, and use agent skills.

GuideBest Security Skills For AI AgentsGuideBest Openclaw Skills For Devops And CICD AutomationGuideBest Openclaw Skills 2026

Remote OpenClaw

AI agent skills directory, marketplace, and workflow hub for OpenClaw, Hermes Agent, Claude Code, Codex, and MCP-powered operator stacks.

The Agent Stack: weekly agent tooling digest, free.

Explore

  • Home
  • Skills Directory
  • Claude Code Skills
  • Codex Skills
  • MCP Clients
  • Marketplace
  • Hermes Ecosystem
  • Free guide
  • Learn
  • Blog
  • The Agent Stack (Digest)

More

  • Submit a Tool
  • Advertise
  • Playbook
  • Free Tools
  • API
  • Shipping
  • Contact
  • Terms
  • Privacy

Know a company that should advertise here? Refer them and earn 10% — up to $300 per referral.

© 2026 Remote OpenClaw
Fazier badgeFeatured on Twelve ToolsFeatured on Wired BusinessRemote OpenClaw - Featured on AI Agents DirectoryListed on Turbo0