按框架
Laravel 安全 — 生产环境加固参考手册
一份 Laravel 生产环境加固参考:按优先级排序的清单、危险的默认设置,以及从密钥、配置到授权、依赖 CVE 的分领域指南,外加一份自查清单。以防御视角讲解,不含攻击步骤。
适用对象:任何在生产环境运行 Laravel 的人。这里不讲攻击步骤——这是一份可直接照做的加固参考:按优先级排序的清单、危险的默认设置、分领域指南,以及自查。关于跨框架的整体图景,参见 按框架的安全防护入口。
按优先级排序的加固清单
从上到下照做这张表。P0 是其余一切的前提,P1 是最频繁的事故来源,P2 是持续的运营卫生。
P0 ── 前提(先做)
生产环境关调试 / 密钥以权限 600 远离公开面 / 管理 APP_KEY
P1 ── 首要事故来源
授权(Policy/Gate)/ Mass Assignment 控制 / 依赖 CVE / 生产环境缓存
P2 ── 运营卫生
会话/CSRF/Cookie / 上传校验 / HTTPS、响应头、限流
| 优先级 | 控制项 | 具体做法(Laravel) |
|---|---|---|
| P0 | 关闭生产环境调试 | APP_DEBUG=false / APP_ENV=production,用 config:cache 固定。别在错误页上泄露内部信息 |
| P0 | 密钥远离公开面 | .env、备份、密钥放在 public/ 之外,权限 600。storage/logs 不公开 |
| P0 | 妥善管理 APP_KEY | 是加密、签名 Cookie、会话的基础。从环境变量注入;泄露时轮换 |
| P1 | 显式授权 | Policy / Gate + authorize() / can 中间件,按所有者/权限划定范围 |
| P1 | 控制 Mass Assignment | 声明 $fillable;避免批量赋值 $request->all(),改用 validated() |
| P1 | 监控 Composer CVE | composer audit / osv-scanner;按运行中的版本判断,快速打补丁 |
| P1 | 生产环境缓存 | config:cache route:cache view:cache,让设置可靠生效并提速 |
| P2 | 会话/Cookie 安全 | 设置 secure http_only same_site;登录时重新生成会话 |
| P2 | 上传校验 | 校验类型/大小,存放在 public/ 之外,无执行权限 |
| P2 | HTTPS/响应头/限流 | 强制 HTTPS、HSTS,在登录/API 上加 throttle |
1. 密钥与 APP_KEY(P0)
Laravel 默认把 .env 放在文档根之外(在项目根目录)。事故大多来自把密钥放到了它们不该在的地方。
- 绝不要把
.env、数据库导出、备份或密钥文件放进public/。密钥要放在应用根之外,权限 600(仅所有者可读)。 - 别暴露
storage/或storage/logs/(日志里可能含密钥或个人数据)。别把.env提交到仓库。 APP_KEY是加密、签名 URL、加密 Cookie 和会话的基础。从环境变量注入它,并在泄露时立即轮换(注意这会使既有的加密数据/会话失效)。
关于通用原则,参见 不把密钥放进公开目录;关于一个真实的全暴露案例,参见 一个 .env 全暴露的案例。
2. 生产配置:DEBUG、环境、缓存(P0)
APP_DEBUG=false / APP_ENV=production
用缓存固定配置
php artisan config:cache(+ route:cache view:cache)让设置可靠生效并加速应用。注意:config:cache 之后,config/ 之外的 env() 会返回 null,所以在应用中要通过 config('...') 引用值。别在生产环境暴露诊断工具
3. 授权:Broken Access Control(P1 — 首要来源)
生产环境最常见的事故是“已认证但未授权”。能登录并不意味着被允许执行该操作。
常见(危险)
- 没有授权——“登录了=能看/能改”
- route-model 绑定取到了别的用户的 ID
Model::create($request->all())接受每一个字段- 像
is_admin这样的权限字段由用户输入赋值
正确
- Policy / Gate +
authorize()/can中间件,每次都按所有者/权限划定范围 - 读取也按所有者划定范围(例如
where('user_id', $me)) - 用
$request->validated()(FormRequest)限制输入 - 声明
$fillable以拦截 Mass Assignment
参见 IDOR 是什么。关键是在每一条读取/更新/删除路径上都做所有者检查。
4. 注入与输出
Laravel 的 Eloquent/查询构造器用占位符绑定值,Blade 默认对输出转义。规则是别自己把这份安全性关掉。
- SQL:别通过字符串拼接把用户输入混进
whereRaw/DB::raw。即便确实需要原生 SQL,也要用绑定(→ SQL 注入是什么)。 - XSS:Blade
{{ }}会转义;{!! !!}是原始输出。别把用户输入传给{!! !!}。若必须输出 HTML,只在净化之后(→ XSS 是什么)。 - 校验:在使用前用 FormRequest /
validate()校验类型/范围/允许值。
5. 会话、CSRF、Cookie(P2)
- CSRF:别全局禁用 web 中间件的 CSRF 保护。表单里用
@csrf,SPA 用恰当的令牌处理(→ CSRF 是什么)。 - Cookie/会话:在
config/session.php里设置secure(HTTPS)、http_only、same_site。登录时重新生成会话以防固定攻击(Laravel 的认证会重新生成)。 - 暴力破解:对登录施加
throttle/ 登录尝试次数限制。
6. 文件上传与公开文件(P2)
- 校验 mime 类型、扩展名和大小,并且别信任客户端提供的文件名。
- 存放在
public/之外(例如storage/),且无执行权限。只对确实需要公开的内容,通过受控路径提供。 - 在交付时也做授权,这样连续的直链就无法取到别的用户的文件。
7. HTTPS、安全响应头、限流(P2)
- 强制 HTTPS(
URL::forceScheme('https')等)+ HSTS。在负载均衡器后面,用TrustProxies以正确识别协议。 - 安全响应头(X-Content-Type-Options 等;围绕你的资源设置来设计 CSP)。用 安全响应头检查器 检查你自己的站点。
- 限流:对登录、API 和密码重置施加
throttle。
8. 依赖与版本(P1)
- 用
composer audit或 osv-scanner 监控 Composer 依赖 CVE,按运行中的版本判断,并快速打补丁(→ 监控依赖 CVE · 漏洞应对实务手册)。 - 让 Laravel 和 PHP 保持在受支持的版本。别把 EOL 版本留着(无法修复的漏洞会累积)。
验证:你的 Laravel 真的加固了吗?
搭好并不是终点——只有在你检查过之后才算完成。这些是针对你自己的站点/环境的防御性自查。
密钥无法通过 URL 取得
/.env 和 /storage/logs/laravel.log,确认它们返回 404(若能取得,立即修复并轮换密钥)。生产环境无调试暴露
授权成立
Cookie/会话与依赖
composer audit 是干净的。本站的视角:即便默认值强,授权与依赖也得靠你自己
Laravel 有许多好的默认值,但授权——谁可以做什么——以及依赖是否保持更新是应用与运营特有的,因此没有任何框架能自动替你守护。我们反复看到的事故,与其说是精巧的攻击,不如说是配置/运营的套路:“已认证,却没有所有者检查”、“生产环境开着调试”、“一个密钥被暴露”。所以重心在于从上到下照做上面那张表,并做那件不起眼的事——让每一次读取/更新都按所有者划定范围,并监控依赖的 CVE。
接下来读
- 入口:按框架的安全防护 · Next.js 安全
- 密钥:不把密钥放进公开目录 · 案例:一个 .env 全暴露的案例
- 授权 / 实操:IDOR 是什么 · 漏洞应对实务手册 · 监控依赖 CVE
- 术语:SQL 注入 · XSS · CSRF
FAQ
Q要保护 Laravel,我该先做什么?
三个 P0 项:(1) 确保生产环境 APP_DEBUG=false,并用 config:cache 把配置固定下来;(2) 让 .env 和密钥文件远离公开目录,并保持严格权限;(3) 妥善管理 APP_KEY(它是加密、签名 Cookie 和会话的基础,泄露时要轮换)。这些是其他一切的前提。接着再转向授权(Policy/Gate)和 Composer 依赖 CVE 监控。
Q带着 APP_DEBUG=true 上线有什么危险?
开着调试时,异常页面不仅会显示堆栈跟踪,还会显示配置值、环境变量、连接信息等内部信息。攻击者可以故意触发错误来把它们提取出来。在生产环境要设 APP_DEBUG=false 和 APP_ENV=production,并用 config:cache 让它固定生效。也不要在生产环境暴露 Telescope、Debugbar 等诊断工具。
Q为什么 config:cache 之后 env() 返回 null?
config:cache 会把配置编译成单个文件以提速,之后在配置文件之外调用的 env() 会返回 null(只读取缓存时刻的 .env)。解决办法是只在 config/ 内部使用 env(),在应用中通过 config('...') 引用值。在生产环境,缓存 config/route/view 是惯例——它让设置可靠生效并加速应用。
Q如何避免『登录了就等于被允许』?
认证(登录)与授权(该操作是否被允许)是两回事。在 Laravel 里,用 Policy/Gate 实现,并用 authorize() 或 can 中间件按所有者或权限对每一次读取/更新/删除划定范围。同时通过声明 $fillable 控制 Mass Assignment,避免批量赋值 $request->all()。没有这些,只要换掉 URL 里的 ID 就能触及别的用户的数据(IDOR)。
Q如何管理 Composer 依赖漏洞?
用 composer audit 或 osv-scanner 对已知 CVE 做机器监控,按运行中的版本判断,并快速打补丁(依据实际安装的东西来决策,而不是 composer.json 的声明)。同时让 Laravel 和 PHP 保持在受支持的版本,别把 EOL 版本留着不管。在事故中,依赖是否保持更新是比精巧攻击更现实的因素。