Communitygithub.com

laravel-security

Laravel-Sicherheitspraktiken – Authentifizierung, Autorisierung, Eloquent-Sicherheit, CSRF, XSS-Schutz, API-Sicherheit und sichere Deployment-Konfigurationen. Verwenden Sie dies bei der Überprüfung von Laravel Auth, Eloquent-Sicherheit, CSRF, XSS, API-Sicherheit oder Deployment-Konfiguration.

Was ist laravel-security?

Diese Skill bietet eine umfassende Anleitung zur Absicherung von Laravel-Anwendungen gegen häufige Sicherheitslücken. Sie deckt alle wesentlichen Bereiche ab: Authentifizierung mit Sanctum, sichere Passwort-Handhabung, Session-Management, Autorisierung mittels Gates und Policies sowie Eloquent-Sicherheit (Mass Assignment, SQL-Injection-Prävention, Attribut-Casting). Darüber hinaus werden CSRF-Schutz, XSS-Prävention in Blade-Templates und API-Sicherheit (Ratenbegrenzung, CORS, Sanctum-Scopes) behandelt. Die Skill ist für Entwickler gedacht, die Laravel-Anwendungen entwickeln oder bereitstellen, und unterstützt die gängigen Pakete wie Sanctum, Passport, Jetstream, Breeze und Laravel Pennant. Sie enthält konkrete Codebeispiele, Best Practices und Warnungen vor unsicheren Konfigurationen, die direkt in Projekten übernommen werden können.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/laravel-security

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Laravel-Sicherheitspraktiken

Umfassende Sicherheitsrichtlinien für Laravel-Anwendungen zum Schutz vor häufigen Sicherheitslücken.

Wann zu aktivieren

  • Einrichten der Laravel-Authentifizierung und -Autorisierung (Sanctum, Passport, Jetstream, Breeze)
  • Implementieren von Benutzerrollen, -berechtigungen und -richtlinien
  • Konfigurieren von Produktionssicherheitseinstellungen und Umgebungsvariablen
  • Überprüfen von Laravel-Anwendungen auf Sicherheitslücken
  • Bereitstellen von Laravel-Anwendungen in der Produktion
  • Schreiben sicherer Eloquent-Abfragen und Migrationen

Produktionskonfiguration

Wesentliche Produktionseinstellungen

// config/app.php
'env' => env('APP_ENV', 'production'),
'debug' => (bool) env('APP_DEBUG', false), // KRITISCH: Niemals true in Produktion
'key' => env('APP_KEY'), // Muss gesetzt sein: php artisan key:generate

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

// APP_KEY beim Booten überprüfen
// bootstrap/app.php oder in einem Service Provider
if (empty(config('app.key'))) {
    throw new RuntimeException('APP_KEY ist nicht gesetzt. Führen Sie aus: php artisan key:generate');
}

Sicherheit der Umgebungsdatei

# .env NIEMALS in die Versionskontrolle einchecken
# .gitignore enthält standardmäßig bereits .env

# Stattdessen .env.example mit Platzhaltern verwenden
DB_PASSWORD=
APP_KEY=
SANCTUM_TOKEN_PREFIX=

# Erforderliche Variablen beim Booten validieren
// 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("Fehlender erforderlicher Konfigurationsschlüssel: {$key}");
    }
}

HTTPS-Erzwingung

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

// config/app.php für vertrauenswürdige Proxies (Load Balancer)
// Bestimmte IP-Bereiche verwenden – * vertraut allen, was X-Forwarded-*-Spoofing ermöglicht
// 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'],

// HTTPS in Produktion über Middleware erzwingen
// 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);
}

Authentifizierung

Sanctum (API-Token-Authentifizierung)

// 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-Ablauf in Minuten (null = nie)
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),

// Tokens mit Fähigkeiten ausstellen
$token = $user->createToken('api-token', ['read', 'write'])->plainTextToken;

// Fähigkeiten auf Routen validieren
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/orders', function () {
        // Benutzer muss 'read'-Fähigkeit haben
        abort_unless(Auth::user()->tokenCan('read'), 403);
        // ...
    })->middleware('abilities:read');

    Route::post('/orders', function () {
        // Benutzer muss 'write'-Fähigkeit haben
        abort_unless(Auth::user()->tokenCan('write'), 403);
        // ...
    })->middleware('abilities:write');
});

Passwortsicherheit

// config/hashing.php
// Standard ist bcrypt. Argon2id ist stärker.
'bcrypt' => [
    'rounds' => env('BCRYPT_ROUNDS', 12), // Erhöhen für stärkeres Hashing
],

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

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

// Login-Versuche ratenbegrenzen
// App\Http\Controllers\Auth\AuthenticatedSessionController
protected function authenticated(Request $request, $user)
{
    if ($user->wasRecentlyLockedOut()) {
        // Benutzer über verdächtigen Login benachrichtigen
        $user->notify(new SuspiciousLoginNotification($request->ip()));
    }
}

Session-Verwaltung

// 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),

// Session bei Login regenerieren
// App\Http\Controllers\Auth\AuthenticatedSessionController
public function store(LoginRequest $request): RedirectResponse
{
    $request->authenticate();
    $request->session()->regenerate(); // KRITISCH: verhindert Session Fixation
    return redirect()->intended(RouteServiceProvider::HOME);
}

// Session bei Logout ungültig machen
public function destroy(Request $request): RedirectResponse
{
    Auth::guard('web')->logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();
    return redirect('/');
}

Autorisierung

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';
    });

    // before() für Super-Admin-Überschreibung verwenden
    Gate::before(function (User $user, string $ability): ?bool {
        if ($user->role === 'super-admin') {
            return true; // Gewährt alle Fähigkeiten
        }
        return null; // Fällt auf normale Prüfungen zurück
    });
}

// Verwendung in Controllern
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);
    // Oder: $this->authorize('update-post', $post);
    // Oder: 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; // Öffentliche Auflistung
    }

    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(); // Muss E-Mail bestätigen
    }

    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';
    }
}

// In AuthServiceProvider registrieren
protected $policies = [
    Post::class => PostPolicy::class,
];

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

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

@cannot('update', $post)
    <span>Sie können diesen Beitrag nicht bearbeiten</span>
@endcannot

Middleware-Autorisierung

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

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

// Benutzerdefinierte Autorisierungs-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, 'Unautorisiert. Dieser Bereich erfordert die Rolle: ' . $role);
        }
        return $next($request);
    }
}

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

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

Eloquent-Sicherheit

Mass Assignment-Schutz

// SCHLECHT: $guarded = [] erlaubt Massenzuweisung für ALLE Spalten
// NIEMALS $guarded = [] in Produktion verwenden

// GUT: Fillable-Attribute auf die Whitelist setzen
final class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'phone',
        'avatar',
    ];
    // NIEMALS 'role', 'is_admin', 'is_verified' hier hinzufügen
}

// GUT: Explizit steuern, welche Felder in Requests befüllt werden können
public function store(StoreUserRequest $request): RedirectResponse
{
    $user = User::create($request->safe()->only([
        'name', 'email', 'phone', 'avatar'
    ]));
    // $request->safe() verwendet nur validierte Daten
    // $request->only() ist ohne Validierungsregeln NICHT sicher
}

// SCHLECHT: Benutzer direkt mit Request-Daten erstellen
User::create($request->all()); // ANGREIFBAR gegen Mass Assignment!

// BESSER: DTOs für die Erstellung verwenden
$user = User::create($request->validated()); // Nur validierte Felder

SQL-Injection-Prävention

// GUT: Eloquent parametrisiert Abfragen automatisch
User::where('email', $userInput)->first();
User::whereRaw('email = ?', [$userInput])->first();

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

// SCHLECHT: Rohe String-Interpolation
DB::select("SELECT * FROM users WHERE email = '{$userInput}'"); // ANGREIFBAR!
User::whereRaw("email = '{$userInput}'")->first(); // ANGREIFBAR!

// SCHLECHT: whereRaw/orderByRaw mit unescaptem Input
User::orderByRaw($userInput); // ANGREIFBAR!
User::groupByRaw($userInput); // ANGREIFBAR!

// SCHLECHT: DB::statement mit Verkettung
DB::statement("INSERT INTO users (email) VALUES ('{$userInput}')"); // ANGREIFBAR!

Attribut-Casting

final class User extends Authenticatable
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean', // Cast zu Boolean verhindert String-Injection
        'settings' => 'array', // Automatisch json_encode/json_decode
        'metadata' => 'encrypted:array', // Laravel 11+ verschlüsseltes Casting
        'password' => 'hashed', // Laravel 10+ automatisches Hashing beim Setzen
    ];
}

Modell-Sicherheit

final class User extends Authenticatable
{
    // Vertrauliche Attribute vor JSON/API-Antworten verbergen
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_secret',
        'two_factor_recovery_codes',
    ];

    // Nur sichere berechnete Attribute anhängen
    protected $appends = ['full_name']; // sicher
    // NIEMALS vertrauliche berechnete Daten anhängen
}

final class Post extends Model
{
    // Global Scope zum Filtern von Soft-Deleted-Datensätzen
    use SoftDeletes;

    // N+1 verhindern, indem Lazy Loading eingeschränkt wird (optionaler Strict-Modus)
    // AppServiceProvider::boot()
    // Model::preventLazyLoading(!app()->isProduction());
}

CSRF-Schutz

Standard-Schutz

// Laravel CSRF ist standardmäßig über die Middleware VerifyCsrfToken aktiviert
// app/Http/Kernel.php (protected $middlewareGroups['web'])

// Alle POST/PUT/PATCH/DELETE-Formulare müssen @csrf enthalten
<form method="POST" action="/posts">
    @csrf
    <input type="text" name="title">
    <button type="submit">Erstellen</button>
</form>

Routen ausschließen (Sorgfältig)

// app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends Middleware
{
    // Nur Routen ausschließen, die externen CSRF-Schutz haben (Webhooks usw.)
    protected $except = [
        'stripe/*', // Stripe-Webhooks verwenden eigene Signaturprüfung
        // Blankett 'api/*' vermeiden — zustandsbehaftete Sanctum-Routen benötigen CSRF.
        // Nur spezifische zustandslose Webhook/Endpoint-Routen ausschließen.
    ];
}

CSRF mit JavaScript

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

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

// Fetch-Beispiel
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-Prävention

Blade-Template-Sicherheit

{{-- SICHER: Automatisch von Blade escapt --}}
{{ $userInput }}

{{-- GEFÄHRLICH: Rohe Ausgabe — NIEMALS mit Benutzereingaben verwenden --}}
{!! $userInput !!}

{{-- SICHER: {!! !!} nur mit vertrauenswürdigen Inhalten verwenden, die Sie kontrollieren --}}
{!! $trustedHtmlFromYourServer !!}

{{-- GUT: Spezifische Escaping-Direktiven verwenden --}}
@js($data) {{-- JSON für JavaScript encodieren --}}
@json($data) {{-- JSON in Templates encodieren --}}

{{-- SCHLECHT: Direkte Benutzereingabe in rohem HTML --}}
<div>{!! $user->bio !!}</div> {{-- ANGREIFBAR, wenn der Benutzer die Bio bereitstellt --}}

Sichere HTML-Verarbeitung

// Wenn Sie etwas HTML erlauben müssen, verwenden Sie einen Whitelist-Ansatz
use HTMLPurifier; // Erfordert: 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>{!! $sanitizedHtml !!}</div> {{-- Jetzt sicher, da es bereinigt wurde --}}

Content Security Policy (CSP)

// CSP-Header über Middleware setzen
// app/Http/Middleware/ContentSecurityPolicy.php
class ContentSecurityPolicy
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);
        
        $response->headers->set('Content-Security-Policy', implode('; ', [
            "default-src 'self'",
            "script-src 'self' 'nonce-" . $this->generateNonce() . "' https://trusted-cdn.com",
            "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
            "img-src 'self' data: https://images.example.com",
            "font-src 'self' https://fonts.gstatic.com",
            "connect-src 'self' https://api.example.com",
            "frame-src 'none'",
            "object-src 'none'",
            "base-uri 'self'",
            "form-action 'self'",
        ]));
        
        return $response;
    }
    
    protected function generateNonce(): string
    {
        return base64_encode(random_bytes(16));
    }
}

// In Kernel registrieren
protected $middlewareGroups = [
    'web' => [
        // ...
        \App\Http\Middleware\ContentSecurityPolicy::class,
    ],
];

API-Sicherheit

Ratenbegrenzung

// app/Providers/RouteServiceProvider.php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

protected function configureRateLimiting(): void
{
    // API-Ratenbegrenzung
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });
    
    // Login-Ratenbegrenzung
    RateLimiter::for('login', function (Request $request) {
        return Limit::perMinute(5)->by($request->input('email') . '|' . $request->ip());
    });
    
    // Spezifische Ratenbegrenzung für sensible Endpunkte
    RateLimiter::for('password-reset', function (Request $request) {
        return Limit::perHour(3)->by($request->input('email'));
    });
}

// In Routen verwenden
Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
    Route::apiResource('orders', OrderController::class);
});

// Benutzerdefinierte Ratenbegrenzungs-Antwort
public function handle(Request $request, Closure $next, $limiterName = 'api'): mixed
{
    $response = $next($request);
    
    if (RateLimiter::tooManyAttempts($limiterName, 60)) {
        $retryAfter = RateLimiter::availableIn($limiterName);
        return response()->json([
            'message' => 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.',
            'retry_after' => $retryAfter,
        ], 429)
        ->header('Retry-After', $retryAfter);
    }
    
    return $response;
}

CORS-Konfiguration

// config/cors.php (Laravel 7+)
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'], // Oder bestimmte Methoden
    'allowed_origins' => env('CORS_ALLOWED_ORIGINS') 
        ? explode(',', env('CORS_ALLOWED_ORIGINS')) 
        : ['https://example.com'], // NIEMALS '*' in Produktion
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => ['X-Pagination-Total', 'X-Pagination-Page'],
    'max_age' => 0,
    'supports_credentials' => true, // Für Sanctum zustandsbehaftete Authentifizierung
];

Sanctum-Token-Fähigkeiten

// Token mit granularen Fähigkeiten definieren
// app/Providers/AuthServiceProvider.php
use Laravel\Sanctum\Sanctum;

public function boot(): void
{
    Sanctum::authenticateAccessTokenUsing(function ($token, $abilities) {
        // Benutzerdefinierte Token-Überprüfungslogik
        if (in_array('*', $abilities)) {
            return true; // Vollzugriff
        }
        
        return collect($abilities)->contains(function ($ability) use ($token) {
            return $token->can($ability);
        });
    });
}

// Abilities für verschiedene Benutzerrollen definieren
class TokenAbility extends Enum
{
    case READ = 'read';
    case WRITE = 'write';
    case ADMIN = 'admin';
    case MANAGE_USERS = 'manage-users';
}

// Token mit bestimmten Fähigkeiten ausstellen
$token = $user->createToken(
    'mobile-app', 
    [TokenAbility::READ->value, TokenAbility::WRITE->value]
);

// Middleware zum Überprüfen mehrerer Fähigkeiten
// app/Http/Middleware/CheckAbilities.php
class CheckAbilities
{
    public function handle(Request $request, Closure $next, ...$abilities): mixed
    {
        if (!$request->user() || !$request->user()->currentAccessToken()) {
            abort(401, 'Nicht authentifiziert.');
        }
        
        foreach ($abilities as $ability) {
            if (!$request->user()->tokenCan($ability)) {
                abort(403, "Fehlende Fähigkeit: {$ability}");
            }
        }
        
        return $next($request);
    }
}

Datenvalidierung

// Form Request-Validierung
// app/Http/Requests/StoreOrderRequest.php
class StoreOrderRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true; // Oder benutzerdefinierte Autorisierungslogik
    }

    public function rules(): array
    {
        return [
            'items' => ['required', 'array', 'min:1', 'max:50'],
            'items.*.product_id' => ['required', 'exists:products,id'],
            'items.*.quantity' => ['required', 'integer', 'min:1', 'max:100'],
            'shipping_address' => ['required', 'string', 'max:500'],
            'payment_method' => ['required', 'in:credit_card,paypal,stripe'],
        ];
    }

    public function messages(): array
    {
        return [
            'items.*.product_id.exists' => 'Eines der Produkte existiert nicht.',
            'items.max' => 'Sie können maximal 50 Artikel pro Bestellung haben.',
        ];
    }
}

Bereitstellungssicherheit

Umgebungsvariablen

# Erforderliche Variablen in der Produktion
APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:generiertenSchlüsselHier
DB_HOST=127.0.0.1
DB_DATABASE=production_db
DB_USERNAME=secure_user
DB_PASSWORD=starkesPasswort
SESSION_SECURE_COOKIE=true
CORS_ALLOWED_ORIGINS=https://example.com

# NIEMALS vertrauliche Werte wie API-Schlüssel hardcodieren
# Umgebungsvariablen von einem sicheren Speicher verwenden (z. B. AWS Secrets Manager)

Dateiberechtigungen

# Sichere Dateiberechtigungen für Laravel
# storage/ und bootstrap/cache/ müssen beschreibbar sein
chmod -R 775 storage bootstrap/cache
chmod -R 750 public
chmod 600 .env

# Eigentümer einstellen
chown -R www-data:www-data /var/www/your-app

# Sensible Dateien schützen
chmod 644 composer.json composer.lock package.json package-lock.json
chmod 644 webpack.mix.js vite.config.js

Sicherheitsheader

// app/Http/Middleware/SecurityHeaders.php
class SecurityHeaders
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);
        
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-Frame-Options', 'DENY'); // Oder SAMEORIGIN
        $response->headers->set('X-XSS-Protection', '1; mode=block');
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
        $response->headers->set('Permissions-Policy', implode(', ', [
            'camera=()',
            'microphone=()',
            'geolocation=()',
            'payment=()',
        ]));
        
        return $response;
    }
}

// In Kernel registrieren
protected $middlewareGroups = [
    'web' => [
        // ...
        \App\Http\Middleware\SecurityHeaders::class,
    ],
];

Überwachung und Protokollierung

Aktivitätsprotokollierung

// composer require spatie/laravel-activitylog

// Aktivität auf Modellen protokollieren
class Post extends Model
{
    use \Spatie\Activitylog\Traits\LogsActivity;
    
    protected static $logAttributes = ['title', 'content', 'is_published'];
    protected static $logOnlyDirty = true;
    protected static $submitEmptyLogs = false;
    
    public function getDescriptionForEvent(string $eventName): string
    {
        return "Beitrag wurde {$eventName}";
    }
}

// Manuelle Protokollierung
activity()
    ->causedBy(Auth::user())
    ->performedOn($post)
    ->withProperties(['ip' => request()->ip(), 'user_agent' => request()->userAgent()])
    ->log('Beitrag wurde aktualisiert');

Fehlerüberwachung

// app/Exceptions/Handler.php
class Handler extends ExceptionHandler
{
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            if (app()->environment('production')) {
                // An Überwachungsdienst senden
                // \Sentry\Laravel\Integration::report($e);
            }
        });

        $this->renderable(function (AuthenticationException $e, Request $request) {
            if ($request->expectsJson()) {
                return response()->json(['message' => 'Nicht authentifiziert.'], 401);
            }
            return redirect()->guest(route('login'));
        });

        $this->renderable(function (AuthorizationException $e, Request $request) {
            if ($request->expectsJson()) {
                return response()->json(['message' => 'Nicht autorisiert.'], 403);
            }
            return redirect()->back()->with('error', 'Sie sind nicht berechtigt, diese Aktion auszuführen.');
        });
    }
}

Sicherheitscheckliste

Vor der Produktion

  • APP_DEBUG auf false gesetzt
  • APP_KEY generiert und gesetzt
  • HTTPS überall erzwungen (Middleware, Trusted Proxies)
  • .env nicht in der Versionskontrolle
  • SESSION_SECURE_COOKIE auf true gesetzt (bei HTTPS)
  • Mass Assignment-Schutz (Fillable/Guarded) korrekt konfiguriert
  • CSRF-Schutz aktiviert und Token in Formularen enthalten
  • Sicherheitsheader konfiguriert (CSP, X-Frame-Options usw.)
  • Ratenbegrenzung für sensible Endpunkte eingerichtet
  • CORS ordnungsgemäß konfiguriert (kein * in Produktion)
  • Dateiberechtigungen korrekt gesetzt
  • Fehlerbehandlung (keine Stacktraces in Produktion)
  • Passwortrichtlinien durchgesetzt (Länge, Komplexität)
  • E-Mail-Verifizierung aktiviert
  • Sanctum-Token-Ablauf konfiguriert
  • Sitzungsablauf und -rotation konfiguriert

Regelmäßige Wartung

  • Abhängigkeiten regelmäßig aktualisieren: composer update
  • Auf Sicherheitslücken prüfen: composer audit
  • php artisan cache:clear nach Konfigurationsänderungen
  • Protokolle auf verdächtige Aktivitäten überprüfen
  • Sanctum-Token regelmäßig rotieren
  • API-Endpunkte auf ordnungsgemäße Autorisierung prüfen
  • CSP-Header aktualisieren, wenn neue externe Ressourcen benötigt werden
  • Logging- und Überwachungssysteme überprüfen

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Verwandte Skills