laravel-security

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Laravel 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
    VerifyCsrfToken
    , security headers via
    SecurityHeaders
    ).
  • Guards and policies enforce access control (
    auth:sanctum
    ,
    $this->authorize
    , policy middleware).
  • Form Requests validate and shape input (
    UploadInvoiceRequest
    ) before it reaches services.
  • Rate limiting adds abuse protection (
    RateLimiter::for('login')
    ) alongside auth controls.
  • Data safety comes from encrypted casts, mass-assignment guards, and signed routes (
    URL::temporarySignedRoute
    +
    signed
    middleware).
  • 中间件提供基础防护(通过
    VerifyCsrfToken
    实现CSRF防护,通过
    SecurityHeaders
    设置安全头)。
  • 守卫(Guards)与策略(Policies)强制执行访问控制(
    auth:sanctum
    $this->authorize
    、策略中间件)。
  • 表单请求(Form Requests)在输入到达服务层前进行验证与格式化(如
    UploadInvoiceRequest
    )。
  • 速率限制与身份验证控制配合,防止滥用(如
    RateLimiter::for('login')
    )。
  • 数据安全通过加密类型转换、批量赋值防护和签名路由(
    URL::temporarySignedRoute
    +
    signed
    中间件)保障。

Core Security Settings

核心安全配置

  • APP_DEBUG=false
    in production
  • APP_KEY
    must be set and rotated on compromise
  • Set
    SESSION_SECURE_COOKIE=true
    and
    SESSION_SAME_SITE=lax
    (or
    strict
    for sensitive apps)
  • 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
    SESSION_HTTP_ONLY=true
    to prevent JavaScript access
  • Use
    SESSION_SAME_SITE=strict
    for high-risk flows
  • Regenerate sessions on login and privilege changes
  • 设置
    SESSION_HTTP_ONLY=true
    以阻止JavaScript访问
  • 高风险流程使用
    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
    Hash::make()
    and never store plaintext
  • 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的密码代理处理重置流程
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

授权:策略与门(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']);
  • 使用策略实现模型级授权
  • 在控制器和服务层中强制执行授权
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
  • 始终使用表单请求验证输入
  • 使用严格的验证规则和类型检查
  • 绝不信任请求负载中的派生字段

Mass Assignment Protection

批量赋值防护

  • Use
    $fillable
    or
    $guarded
    and avoid
    Model::unguard()
  • Prefer DTOs or explicit attribute mapping
  • 使用
    $fillable
    $guarded
    ,避免使用
    Model::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
    VerifyCsrfToken
    middleware enabled
  • Include
    @csrf
    in forms and send XSRF tokens for SPA requests
For SPA authentication with Sanctum, ensure stateful requests are configured:
php
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),
  • 保持
    VerifyCsrfToken
    中间件启用状态
  • 在表单中包含
    @csrf
    ,并为SPA请求发送XSRF令牌
使用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
    throttle
    middleware on auth and write endpoints
  • 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',
];
对静态存储的敏感列使用加密类型转换。
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
    composer audit
    regularly
  • 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');