laravel-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLaravel Security Best Practices
Laravel安全最佳实践
Comprehensive security guidance for Laravel applications to protect against common vulnerabilities.
为Laravel应用提供全面的安全指导,防范常见漏洞。
When to Activate
适用场景
- Adding authentication or authorization
- Handling user input and file uploads
- Building new API endpoints
- Managing secrets and environment settings
- Hardening production deployments
- 添加身份验证或授权机制时
- 处理用户输入与文件上传时
- 构建新API接口时
- 管理密钥与环境配置时
- 加固生产环境部署时
How It Works
实现机制
- Middleware provides baseline protections (CSRF via , security headers via
VerifyCsrfToken).SecurityHeaders - Guards and policies enforce access control (,
auth:sanctum, policy middleware).$this->authorize - Form Requests validate and shape input () before it reaches services.
UploadInvoiceRequest - Rate limiting adds abuse protection () alongside auth controls.
RateLimiter::for('login') - Data safety comes from encrypted casts, mass-assignment guards, and signed routes (+
URL::temporarySignedRoutemiddleware).signed
- 中间件提供基础防护(通过实现CSRF防护,通过
VerifyCsrfToken设置安全头)。SecurityHeaders - 守卫(Guards)与策略(Policies)强制执行访问控制(如、
auth:sanctum、策略中间件)。$this->authorize - 表单请求(Form Requests)在输入抵达业务服务前完成验证与格式化(如)。
UploadInvoiceRequest - 速率限制在身份验证控制之外额外提供滥用防护(如)。
RateLimiter::for('login') - 数据安全由加密字段类型转换、批量赋值防护及签名路由(+
URL::temporarySignedRoute中间件)保障。signed
Core Security Settings
核心安全配置
- in production
APP_DEBUG=false - must be set and rotated on compromise
APP_KEY - Set and
SESSION_SECURE_COOKIE=true(orSESSION_SAME_SITE=laxfor sensitive apps)strict - Configure trusted proxies for correct HTTPS detection
- 生产环境中设置
APP_DEBUG=false - 必须设置,且在密钥泄露后立即轮换
APP_KEY - 设置及
SESSION_SECURE_COOKIE=true(敏感应用可设为SESSION_SAME_SITE=lax)strict - 配置可信代理以确保HTTPS检测准确
Session and Cookie Hardening
会话与Cookie加固
- Set to prevent JavaScript access
SESSION_HTTP_ONLY=true - Use for high-risk flows
SESSION_SAME_SITE=strict - Regenerate sessions on login and privilege changes
- 设置以阻止JavaScript访问会话Cookie
SESSION_HTTP_ONLY=true - 高风险流程中使用
SESSION_SAME_SITE=strict - 用户登录或权限变更时重新生成会话
Authentication and Tokens
身份验证与令牌
- Use Laravel Sanctum or Passport for API auth
- Prefer short-lived tokens with refresh flows for sensitive data
- Revoke tokens on logout and compromised accounts
Example route protection:
php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware('auth:sanctum')->get('/me', function (Request $request) {
return $request->user();
});- 使用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();
});Password Security
密码安全
- Hash passwords with and never store plaintext
Hash::make() - Use Laravel's password broker for reset flows
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'])]);- 使用哈希存储密码,绝对禁止明文存储
Hash::make() - 使用Laravel的密码代理(password broker)实现重置流程
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'])]);Authorization: Policies and Gates
授权:策略与门限
- Use policies for model-level authorization
- Enforce authorization in controllers and services
php
$this->authorize('update', $project);Use policy middleware for route-level enforcement:
php
use Illuminate\Support\Facades\Route;
Route::put('/projects/{project}', [ProjectController::class, 'update'])
->middleware(['auth:sanctum', 'can:update,project']);- 使用策略(Policies)实现模型级授权
- 在控制器与业务服务中强制执行授权校验
php
$this->authorize('update', $project);使用策略中间件实现路由级授权校验:
php
use Illuminate\Support\Facades\Route;
Route::put('/projects/{project}', [ProjectController::class, 'update'])
->middleware(['auth:sanctum', 'can:update,project']);Validation and Data Sanitization
输入验证与数据清洗
- Always validate inputs with Form Requests
- Use strict validation rules and type checks
- Never trust request payloads for derived fields
- 始终使用表单请求(Form Requests)验证输入
- 使用严格的验证规则与类型检查
- 绝不要信任请求载荷中的衍生字段
Mass Assignment Protection
批量赋值防护
- Use or
$fillableand avoid$guardedModel::unguard() - Prefer DTOs or explicit attribute mapping
- 使用或
$fillable定义可赋值字段,避免使用$guardedModel::unguard() - 优先使用DTO(数据传输对象)或显式属性映射
SQL Injection Prevention
SQL注入防护
- Use Eloquent or query builder parameter binding
- Avoid raw SQL unless strictly necessary
php
DB::select('select * from users where email = ?', [$email]);- 使用Eloquent或查询构建器的参数绑定
- 除非绝对必要,否则避免使用原生SQL
php
DB::select('select * from users where email = ?', [$email]);XSS Prevention
XSS防护
- Blade escapes output by default ()
{{ }} - Use only for trusted, sanitized HTML
{!! !!} - Sanitize rich text with a dedicated library
- Blade模板默认自动转义输出(语法)
{{ }} - 仅对可信且已清洗的HTML使用语法
{!! !!} - 使用专用库清洗富文本内容
CSRF Protection
CSRF防护
- Keep middleware enabled
VerifyCsrfToken - Include in forms and send XSRF tokens for SPA requests
@csrf
For SPA authentication with Sanctum, ensure stateful requests are configured:
php
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),- 保持中间件启用状态
VerifyCsrfToken - 表单中添加指令,SPA请求中携带XSRF令牌
@csrf
使用Sanctum实现SPA身份验证时,确保配置有状态请求:
php
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),File Upload Safety
文件上传安全
- Validate file size, MIME type, and extension
- Store uploads outside the public path when possible
- Scan files for malware if required
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
);- 验证文件的大小、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
);Rate Limiting
速率限制
- Apply middleware on auth and write endpoints
throttle - Use stricter limits for login, password reset, and 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'))),
];
});- 对身份验证与写入型接口应用中间件
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'))),
];
});Secrets and Credentials
密钥与凭证管理
- Never commit secrets to source control
- Use environment variables and secret managers
- Rotate keys after exposure and invalidate sessions
- 绝不要将密钥提交到版本控制系统
- 使用环境变量与密钥管理器存储敏感信息
- 密钥泄露后立即轮换并失效所有会话
Encrypted Attributes
加密字段
Use encrypted casts for sensitive columns at rest.
php
protected $casts = [
'api_token' => 'encrypted',
];对敏感数据库列使用加密类型转换(encrypted casts)实现静态加密。
php
protected $casts = [
'api_token' => 'encrypted',
];Security Headers
安全头
- Add CSP, HSTS, and frame protection where appropriate
- Use trusted proxy configuration to enforce HTTPS redirects
Example middleware to set headers:
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;
}
}- 按需添加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 and API Exposure
CORS与API暴露控制
- Restrict origins in
config/cors.php - Avoid wildcard origins for authenticated routes
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,
];- 在中限制允许的请求源
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,
];Logging and PII
日志与个人可识别信息(PII)
- Never log passwords, tokens, or full card data
- Redact sensitive fields in structured logs
php
use Illuminate\Support\Facades\Log;
Log::info('User updated profile', [
'user_id' => $user->id,
'email' => '[REDACTED]',
'token' => '[REDACTED]',
]);- 绝不要在日志中记录密码、令牌或完整银行卡数据
- 结构化日志中对敏感字段进行脱敏处理
php
use Illuminate\Support\Facades\Log;
Log::info('User updated profile', [
'user_id' => $user->id,
'email' => '[REDACTED]',
'token' => '[REDACTED]',
]);Dependency Security
依赖安全
- Run regularly
composer audit - Pin dependencies with care and update promptly on CVEs
- 定期运行检测依赖包漏洞
composer audit - 谨慎固定依赖版本,发现CVE时及时更新
Signed URLs
签名URL
Use signed routes for temporary, tamper-proof links.
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');使用签名路由生成临时、防篡改的链接。
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');