如何对ThinkPHP6应用进行安全加固

文章导读
ThinkPHP6 默认带了不少方便开发的功能,这些功能放到公网环境里可能就是攻击者的入口。如果你的应用已经上线,或者准备部署到非本机环境,建议先按下面几个顺序检查。这里不承诺能挡住所有攻击,但至少可以把一些明显的配置问题和代码习惯堵住。这套思路适合用 ThinkPHP6 开发的中小型 Web 应用,尤其是部署在公网服务器上的项目。先处理调试模式、强制路由和 SQL 拼接,是因为这三个点最容易确认
📋 目录
  1. 适用场景与加固顺序
  2. 先关掉调试模式,错误页别当情报页
  3. 强制路由,别让控制器类名变成公开地址
  4. SQL 注入核查,关键在拼接处
  5. 下一步:上传、CSRF、依赖库
A A

适用场景与加固顺序

ThinkPHP6 默认带了不少方便开发的功能,这些功能放到公网环境里可能就是攻击者的入口。如果你的应用已经上线,或者准备部署到非本机环境,建议先按下面几个顺序检查。这里不承诺能挡住所有攻击,但至少可以把一些明显的配置问题和代码习惯堵住。这套思路适合用 ThinkPHP6 开发的中小型 Web 应用,尤其是部署在公网服务器上的项目。先处理调试模式、强制路由和 SQL 拼接,是因为这三个点最容易确认,也直接影响攻击者“能不能看到内部结构”和“能不能直接拼语句”。它们不需要引入额外组件,改的是框架配置和查询代码,风险相对可控。

先关掉调试模式,错误页别当情报页

生产环境里如果 .env 中的 APP_DEBUG 为 true,ThinkPHP6 发生异常时会展示完整调用栈和 SQL 语句,攻击者可直接读取数据库表名、字段名以及服务器目录结构。判断方法:访问一个不存在的路由,观察响应是否包含详细错误类名和路径。加固时把 APP_DEBUG 改为 false,并在部署脚本中强制覆盖该环境变量。常见坑是修改 .env 后未执行 php think clear,框架仍读取旧配置。建议在版本控制之外管理 .env,并让环境变量优先于文件配置。

只改配置还不够,还要确认部署流程里不会因为某种原因自动把 APP_DEBUG 改回 true。比如有些开发者习惯用 .env.example 同步配置,上线时复制过去,如果 .env.example 里写的是 true,就会带上去。另一个常见问题是改了 .env 之后没有执行 php think clear,框架还在用旧的缓存配置。判断方式很简单:故意访问一个不存在控制器或路由,如果页面里出现完整调用栈,说明调试模式还在;如果只看到 404 或自定义错误页,才算关干净。

如何对ThinkPHP6应用进行安全加固

强制路由,别让控制器类名变成公开地址

ThinkPHP6 默认允许 /模块/控制器/操作 的方式直接访问类方法,这意味着只要知道类名和方法名就能执行任意控制器操作,相当于隐藏后门。加固第一步是在 config/route.php 中开启 url_route_must 强制路由,未匹配规则的 URL 一律返回 404。路由定义中应明确指定 get、post 等 HTTP 谓词,避免使用 any 方法。常见坑是开发时为了调试临时注释掉自定义路由,上线后忘记恢复,导致强制路由失效,应用又回到默认访问模式。生产环境还应集中管理路由文件,并禁止在控制器中手动调用跳转方法。

开启强制路由后,应用内的跳转和 URL 生成也要跟着调整。比如后台不少代码用了 url('index/index') 这类写法,如果路由定义的是 index/index,那没问题;但如果你把某个操作定义成 news/read?id=1,而代码里写的是 index/read,就会生成一个不匹配的地址。最稳妥的办法是统一所有入口都走路由,并且每个路由都限定请求方法。验证方式是把未定义路由的 URL 直接访问,确认返回 404,而不是触发控制器。

如何对ThinkPHP6应用进行安全加固

SQL 注入核查,关键在拼接处

检查项目里是否存在字符串拼接拼接 SQL 的代码,可搜索 where、order、group 等关键字附近的拼接写法。例如 where('id = '. $id) 就属于高危用法。ThinkPHP6 的查询构造器支持参数绑定,安全写法是 ->where('id', $id)。如果必须写原生 SQL,应使用 query 方法并传入占位符与独立参数数组。风险边界在于框架仅对查询构造器转义,任何手写拼接都会直接裸奔。另一个容易被忽略的注入点是 order 和 group 字段,它们无法用预处理,建议通过白名单限定排序字段和排序方向。

在代码里搜索 where( 时,能发现大部分拼接点。但要注意一种情况:有些项目使用 ThinkPHP6 的 whereRawexp,这些方法本身允许写原生表达式,如果参数来自用户输入,同样需要手动绑定或过滤。另外,ordergroup 字段无法通过查询构造器自动转义,所以建议单独写一个白名单函数,把允许排序的字段列出来,不在列表里的就强制走默认值。这个动作不需要改动太多代码,但对后台列表页很关键。

如何对ThinkPHP6应用进行安全加固

下一步:上传、CSRF、依赖库

上面三个点处理完之后,基础入口和查询已经干净不少。但还有几处容易忽略的地方。上传文件时,除了限制后缀,最好通过 finfo_open 检查真实 MIME,并把存储目录放到没有执行权限的位置。CSRF 的 token 要放在全局中间件检查,并且确保跟会话绑定。依赖库方面,可以定期运行 composer audit 查看已知漏洞,升级时用 composer install --no-dev 保持环境一致,不要直接 composer update。这些点需要结合业务逐项确认,但顺序可以放在前面三步之后。

安全加固没有一次性做完的说法,每次改动都需要重新验证访问和功能是否正常。如果某次改动影响了线上操作,先回滚配置再看代码,别急着继续往下调。