Files
everything-claude-code/docs/zh-CN/skills/laravel-security/SKILL.md

286 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: laravel-security
description: Laravel 安全最佳实践,涵盖认证/授权、验证、CSRF、批量赋值、文件上传、密钥管理、速率限制和安全部署。
origin: ECC
---
# Laravel 安全最佳实践
针对 Laravel 应用程序的全面安全指导,以防范常见漏洞。
## 何时启用
* 添加身份验证或授权时
* 处理用户输入和文件上传时
* 构建新的 API 端点时
* 管理密钥和环境设置时
* 强化生产环境部署时
## 工作原理
* 中间件提供基础保护(通过 `VerifyCsrfToken` 实现 CSRF通过 `SecurityHeaders` 实现安全标头)。
* 守卫和策略强制执行访问控制(`auth:sanctum``$this->authorize`、策略中间件)。
* 表单请求在输入到达服务之前进行验证和整形(`UploadInvoiceRequest`)。
* 速率限制在身份验证控制之外增加滥用保护(`RateLimiter::for('login')`)。
* 数据安全来自加密转换、批量赋值保护以及签名路由(`URL::temporarySignedRoute` + `signed` 中间件)。
## 核心安全设置
* 生产环境中设置 `APP_DEBUG=false`
* `APP_KEY` 必须设置,并在泄露时轮换
* 设置 `SESSION_SECURE_COOKIE=true``SESSION_SAME_SITE=lax`(对于敏感应用,使用 `strict`
* 配置受信任的代理以正确检测 HTTPS
## 会话和 Cookie 强化
* 设置 `SESSION_HTTP_ONLY=true` 以防止 JavaScript 访问
* 对高风险流程使用 `SESSION_SAME_SITE=strict`
* 在登录和权限变更时重新生成会话
## 身份验证与令牌
* 使用 Laravel Sanctum 或 Passport 进行 API 身份验证
* 对于敏感数据,优先使用带有刷新流程的短期令牌
* 在注销和账户泄露时撤销令牌
路由保护示例:
```php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware('auth:sanctum')->get('/me', function (Request $request) {
return $request->user();
});
```
## 密码安全
* 使用 `Hash::make()` 哈希密码,切勿存储明文
* 使用 Laravel 的密码代理进行重置流程
```php
use 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);
```
使用策略中间件进行路由级强制执行:
```php
use Illuminate\Support\Facades\Route;
Route::put('/projects/{project}', [ProjectController::class, 'update'])
->middleware(['auth:sanctum', 'can:update,project']);
```
## 验证与数据清理
* 始终使用表单请求验证输入
* 使用严格的验证规则和类型检查
* 切勿信任请求负载中的派生字段
## 批量赋值保护
* 使用 `$fillable``$guarded`,避免使用 `Model::unguard()`
* 优先使用 DTO 或显式的属性映射
## SQL 注入防范
* 使用 Eloquent 或查询构建器的参数绑定
* 除非绝对必要,避免使用原生 SQL
```php
DB::select('select * from users where email = ?', [$email]);
```
## XSS 防范
* Blade 默认转义输出(`{{ }}`
* 仅对可信的、已清理的 HTML 使用 `{!! !!}`
* 使用专用库清理富文本
## CSRF 保护
* 保持 `VerifyCsrfToken` 中间件启用
* 在表单中包含 `@csrf`,并为 SPA 请求发送 XSRF 令牌
对于使用 Sanctum 的 SPA 身份验证,确保配置了有状态请求:
```php
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),
```
## 文件上传安全
* 验证文件大小、MIME 类型和扩展名
* 尽可能将上传文件存储在公开路径之外
* 如果需要,扫描文件以查找恶意软件
```php
final 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` 中间件
* 对登录、密码重置和 OTP 使用更严格的限制
```php
use 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'))),
];
});
```
## 密钥与凭据
* 切勿将密钥提交到源代码管理
* 使用环境变量和密钥管理器
* 密钥暴露后及时轮换,并使会话失效
## 加密属性
对静态的敏感列使用加密转换。
```php
protected $casts = [
'api_token' => 'encrypted',
];
```
## 安全标头
* 在适当的地方添加 CSP、HSTS 和框架保护
* 使用受信任的代理配置来强制执行 HTTPS 重定向
设置标头的中间件示例:
```php
use 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;
}
}
```
## CORS 与 API 暴露
*`config/cors.php` 中限制来源
* 对于经过身份验证的路由,避免使用通配符来源
```php
// 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,
];
```
## 日志记录与 PII
* 切勿记录密码、令牌或完整的卡片数据
* 在结构化日志中编辑敏感字段
```php
use Illuminate\Support\Facades\Log;
Log::info('User updated profile', [
'user_id' => $user->id,
'email' => '[REDACTED]',
'token' => '[REDACTED]',
]);
```
## 依赖项安全
* 定期运行 `composer audit`
* 谨慎固定依赖项版本,并在出现 CVE 时及时更新
## 签名 URL
使用签名路由生成临时的、防篡改的链接。
```php
use Illuminate\Support\Facades\URL;
$url = URL::temporarySignedRoute(
'downloads.invoice',
now()->addMinutes(15),
['invoice' => $invoice->id]
);
```
```php
use Illuminate\Support\Facades\Route;
Route::get('/invoices/{invoice}/download', [InvoiceController::class, 'download'])
->name('downloads.invoice')
->middleware('signed');
```