Laravel 自带的安全机制在框架层面做得相当出色,但很多开发者过度信任框架,反而在配置和业务逻辑上留下致命漏洞。我见过太多项目把 ".env" 文件权限设为 777,或者在表单里完全依赖 CSRF 令牌而忽略了更基础的输入校验。Laravel 的 "mass assignment" 保护默认是开启的,但你在 "Model" 里把 "$guarded" 设为空数组的那一刻,就等于把数据库大门钥匙交给了用户。

Eloquent ORM 的 "update()" 方法配合 "request()->all()" 是典型的反模式。即使你用 "$fillable" 白名单,也要警惕用户通过请求参数篡改 "is_admin"、"role_id" 这类敏感字段。我建议在控制器里显式列出允许的字段,而不是偷懒用 "$request->only()" 直接传入模型。更隐蔽的是 JSON 字段的注入,如果你用 MySQL 5.7 以上版本的 JSON 类型,攻击者可以通过精心构造的 JSON 载荷修改嵌套属性,而你的 "$fillable" 白名单根本拦不住这种操作。

XSS 防护方面,Blade 模板的 "{{ }}" 双花括号默认做了 "htmlspecialchars" 转义,这很好。但一旦你用了 "{!! !!}" 来输出未转义内容,就必须确保数据源绝对可信。常见翻车场景是富文本编辑器,开发者以为用 "strip_tags()" 就够了,却忽略了 "onmouseover"、"onerror" 这类事件属性。正确的做法是用 HTML Purifier 库做白名单过滤,只允许特定标签和属性通过。另外,Vue.js 和 Blade 混用时,"v-html" 指令同样会绕过 Laravel 的转义,前后端分离场景下这个坑踩的人特别多。

SQL 注入在 Laravel 里已经很少见了,Eloquent 的参数绑定做得很好。但 "DB::raw()" 和 "orderBy()" 的动态参数仍然是重灾区。我见过有代码这样写:"User::orderBy(request('sort'))->get()",用户传入 "sort=id; DROP TABLE users--" 就能直接执行恶意 SQL。"orderBy" 方法不接受参数绑定,你必须用白名单校验排序字段。同理,"whereRaw"、"havingRaw" 这类方法也要尽量避免拼接用户输入。

CSRF 保护默认全局开启,但 "VerifyCsrfToken" 中间件的 "$except" 数组经常被滥用。很多开发者把整个 API 路由组都排除在 CSRF 检查之外,理由是“API 用 Token 认证不需要 CSRF”。这忽略了浏览器环境下 Cookie 仍然会被自动携带的事实。如果你的 API 同时支持 Cookie 和 Token 认证,排除 CSRF 就等于给跨站请求敞开后门。正确的做法是 API 路由完全不用 Cookie 认证,或者用 Laravel Sanctum 的 SPA 认证模式,它专门处理了这种场景。

文件上传是另一个重灾区。Laravel 的 "store()" 和 "storeAs()" 方法很方便,但如果你直接用用户提供的文件名,攻击者可以上传 "../../.env" 这样的路径穿越文件名。即使 Laravel 默认会生成随机文件名,很多开发者为了业务需求又改回了原始文件名。更隐蔽的是 SVG 文件上传,SVG 本质是 XML 文档,可以内嵌 "<script>" 标签,上传后直接访问就能触发 XSS。解决方案要么禁止 SVG 上传,要么用 SVG 净化器剥离脚本标签,要么强制设置 Content-Disposition 头让浏览器下载而非渲染。

加密与哈希这块,Laravel 的 "Hash" facade 用的是 bcrypt 算法,"Crypt" facade 用的是 AES-256-CBC。但 "APP_KEY" 的管理往往被忽视。这个密钥一旦泄露,所有加密的 Cookie、Session 数据、以及你用 "encrypt()" 加密的数据库字段全都能被解密。".env" 文件不应该被提交到版本控制,生产环境的 "APP_KEY" 应该通过环境变量注入。另外,很多开发者不知道 Laravel 的 Cookie 加密是默认开启的,如果你把敏感数据直接存 Cookie 里,虽然加密了,但 Cookie 体积会膨胀,而且一旦 "APP_KEY" 轮换,所有用户 Cookie 都会失效。

Session 安全配置在 "config/session.php" 里,"secure" 选项决定是否只在 HTTPS 下传输 Cookie,"http_only" 决定 JavaScript 能否访问,"same_site" 控制跨站请求是否携带 Cookie。生产环境这三项应该分别设为 "true"、"true"、"lax" 或 "strict"。但 "same_site" 设为 "strict" 会导致从外部链接点进来时用户处于未登录状态,所以 "lax" 是更平衡的选择。另外,用数据库或 Redis 做 Session 驱动比文件驱动更安全,文件驱动在共享主机上可能被其他租户读取。

CORS 配置错误在前后端分离项目里极其常见。"config/cors.php" 里的 "allowed_origins" 设为 "['*']" 意味着任何域名都能跨域请求你的 API,配合 "supports_credentials" 设为 "true" 时,浏览器会直接拒绝请求,这是浏览器的安全策略。但如果你用自定义中间件手动设置 "Access-Control-Allow-Origin: *" 和 "Access-Control-Allow-Credentials: true",反而会造成安全隐患。正确的做法是明确列出允许的域名,不要图省事用通配符。

Rate Limiting 限流是 Laravel 8 之后内置的功能,"RateLimiter" facade 配合中间件能有效防止暴力破解和 API 滥用。登录接口至少要限制每个 IP 每分钟尝试次数,注册接口也要限制同一 IP 的注册频率。但很多项目只在路由上加了 "throttle:60,1",却没有针对敏感操作单独设置更严格的限流策略。密码重置接口、短信验证码发送接口、文件上传接口都应该有独立的限流规则。

依赖包安全往往被忽略。Laravel 项目通常有上百个 Composer 依赖,一个废弃的包或者有已知漏洞的包就能让整个应用沦陷。"composer audit" 命令能检查已知安全漏洞,应该集成到 CI 流程里。另外,"debug" 模式在生产环境必须关闭,"APP_DEBUG=true" 会泄露数据库密码、密钥、完整的请求堆栈信息。我见过有项目把 "APP_DEBUG" 设为 "true" 只是为了在线上看错误详情,正确的做法是用 Laravel Telescope 或日志聚合工具来监控线上错误。

还有一个容易被忽视的细节是 "TrustProxies" 中间件。如果你用负载均衡或 CDN,Laravel 需要信任代理服务器转发的请求头。"config/trustedproxy.php" 里如果配置不当,攻击者可以通过伪造 "X-Forwarded-For" 头来绕过 IP 限制、伪造请求来源。正确的配置是明确指定代理服务器的 IP 地址或 CIDR 范围,而不是用 "*" 信任所有代理。

队列任务的安全性也值得关注。序列化的 Job 数据存储在 Redis 或数据库中,如果 Job 的构造函数接收了敏感数据,这些数据会以序列化形式存储。攻击者如果能访问队列存储,就能反序列化出敏感信息。Laravel 的 "SerializesModels" trait 只会存储模型 ID 并在执行时重新查询,但自定义的 DTO 对象会被完整序列化。建议传给 Job 的数据在入库前做脱敏处理,或者用 Laravel 的 "encrypted" 队列连接。

最后说说定时任务的安全。"app/Console/Kernel.php" 里定义的 Artisan 命令如果包含用户可控参数,必须做严格的输入校验。"$schedule->command()" 和 "$schedule->exec()" 里的命令拼接尤其危险。如果命令参数来自数据库或外部 API,攻击者注入恶意参数就能在服务器上执行任意命令。"escapeshellarg()" 是必须的,但更好的做法是避免在定时任务中使用动态命令参数。

Laravel 给了我们一套很完善的安全工具,但工具再好也架不住配置错误和偷懒的代码。安全不是加个中间件就完事,而是贯穿在每一行代码、每一个配置项里的持续过程。定期审计 "config" 目录下的所有文件、检查 "$except" 数组、审查 "DB::raw" 的使用、确保文件上传路径安全,这些基础工作比任何高级安全方案都更有效。