api-security
API security patterns for Laravel. Rate limiting, headers, CORS, Sanctum tokens, input validation. Use when building or securing API endpoints.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
API security patterns for Laravel. Rate limiting, headers, CORS, Sanctum tokens, input validation. Use when building or securing API endpoints.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
PHP Artisan CLI patterns including make commands, flags, custom commands, and AI-friendly usage.
Advanced Eloquent ORM patterns including relationships, eager loading, factories, and query optimization.
Laravel development best practices covering service providers, dependency injection, facades, and the Laravel Way.
Laravel Tinker best practices for debugging, testing ideas, and data exploration. When and how to use safely.
Laravel-native error handling patterns. Renderable exceptions, Livewire traits strategies, and user-facing notifications.
Integrate Anthropic Claude API with Laravel. HTTP client, message format, error handling. Use when calling Claude models from Laravel applications.
基于 SOC 职业分类
| name | api-security |
| description | API security patterns for Laravel. Rate limiting, headers, CORS, Sanctum tokens, input validation. Use when building or securing API endpoints. |
Secure your APIs against abuse, injection, and unauthorized access.
// Create token
$token = $user->createToken('api-token', ['read', 'write'])->plainTextToken;
// Protect routes
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn () => auth()->user());
Route::apiResource('posts', PostController::class);
});
// Ability check
if ($user->tokenCan('write')) {
// Allowed
}
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1'
)),
// config/cors.php
'supports_credentials' => true,
// Frontend must call /sanctum/csrf-cookie first
await fetch('/sanctum/csrf-cookie');
await fetch('/login', { method: 'POST', credentials: 'include' });
// ✅ Short-lived tokens for sensitive operations
$token = $user->createToken('payment', ['payments'])->plainTextToken;
// ✅ Prune expired tokens
$user->tokens()->where('last_used_at', '<', now()->subDays(30))->delete();
// ✅ Revoke on password change
$user->tokens()->delete();
// app/Providers/RouteServiceProvider.php
protected function configureRateLimiting(): void
{
// General API limit
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)
->by($request->user()?->id ?: $request->ip());
});
// Strict limit for auth endpoints
RateLimiter::for('auth', function (Request $request) {
return Limit::perMinute(5)->by($request->ip());
});
// Per-user tier limits
RateLimiter::for('tiered', function (Request $request) {
$user = $request->user();
return match ($user?->plan) {
'premium' => Limit::perMinute(1000)->by($user->id),
'pro' => Limit::perMinute(100)->by($user->id),
default => Limit::perMinute(20)->by($request->ip()),
};
});
}
// routes/api.php
Route::middleware(['throttle:auth'])->group(function () {
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
});
Route::middleware(['auth:sanctum', 'throttle:tiered'])->group(function () {
Route::apiResource('posts', PostController::class);
});
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
Retry-After: 60
return [
// ✅ Specific domains, not '*'
'allowed_origins' => [
'https://app.example.com',
'https://admin.example.com',
],
// ✅ Specific methods
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
// ✅ Only needed headers
'allowed_headers' => [
'Content-Type',
'Authorization',
'X-Requested-With',
'Accept',
],
// ✅ Expose rate limit headers
'exposed_headers' => [
'X-RateLimit-Limit',
'X-RateLimit-Remaining',
],
// ✅ For cookie-based auth
'supports_credentials' => true,
'max_age' => 86400, // 24 hours
];
'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS', '')),
// app/Http/Middleware/ApiSecurityHeaders.php
class ApiSecurityHeaders
{
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');
$response->headers->set('X-XSS-Protection', '0'); // Modern browsers
$response->headers->set('Cache-Control', 'no-store');
$response->headers->set('Pragma', 'no-cache');
return $response;
}
}
protected $middlewareGroups = [
'api' => [
// ... other middleware
\App\Http\Middleware\ApiSecurityHeaders::class,
],
];
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string', 'max:65535'],
'status' => ['required', Rule::enum(PostStatus::class)],
];
}
}
// app/Exceptions/Handler.php
protected function invalidJson($request, ValidationException $exception): JsonResponse
{
return response()->json([
'message' => 'Validation failed',
'errors' => $exception->errors(),
], 422);
}
// ❌ Bad - Exposes internal structure
return response()->json([
'user' => $user, // May include sensitive fields
'debug' => $exception->getTrace(),
]);
// ✅ Good - Controlled response
return response()->json([
'user' => new UserResource($user), // API Resource
]);
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// Never: password, remember_token, api_token
];
}
}
class StripeWebhookController extends Controller
{
public function handle(Request $request)
{
$signature = $request->header('Stripe-Signature');
$payload = $request->getContent();
$secret = config('services.stripe.webhook_secret');
try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$signature,
$secret
);
} catch (\Exception $e) {
abort(400, 'Invalid signature');
}
// Process event...
}
}
// app/Http/Middleware/ApiVersion.php
public function handle(Request $request, Closure $next)
{
$version = $request->header('Accept-Version', 'v1');
$request->attributes->set('api_version', $version);
return $next($request);
}
Route::prefix('v1')->group(function () {
Route::apiResource('posts', V1\PostController::class);
});
Route::prefix('v2')->group(function () {
Route::apiResource('posts', V2\PostController::class);
});
Remember: APIs are public attack surfaces. Every endpoint is a potential entry point.