必须将config.php等敏感文件移出Web可访问路径,即Web服务器root指向public/目录,使config/、application/等目录天然不可通过URL访问;辅以.htaccess权限拦截和defined()运行时校验三重防护。

ThinkPHP 的 config.php(或 database.php 等)必须防直接访问,否则数据库密码、API 密钥会裸奔。光靠“不放 public 目录下”不够,得组合权限、路径、运行时校验三层防护。
为什么 config.php 能被直接下载?
常见错误现象:http://example.com/config.php 打开后显示明文密码;curl -I http://example.com/runtime/log/202604/log.php 返回 200;甚至能列出 /runtime/cache/ 目录索引。
根本原因不是 ThinkPHP 漏洞,而是部署方式没隔离敏感路径:
- 用
php -S localhost:8000 router.php启动时没配路由,所有文件都可直访 - Apache/Nginx 把项目根目录设成了
/www/wwwroot/example.com/,而config.php就在该目录下 - 宝塔面板里把网站根目录选错成项目根目录,而非
public/子目录
最有效的保护:把 config.php 移出 Web 可访问路径
ThinkPHP 官方推荐且最省心的做法:让 config.php 根本不在 Web Server 的 DocumentRoot 下。
立即学习“PHP免费学习笔记(深入)”;
正确项目结构应为:
/www/wwwroot/example.com/ ├── application/ # 不在 public 下,安全 ├── config/ # 同上,含 database.php ├── runtime/ ├── public/ # ← Nginx/Apache 的 root 必须指向这里 │ ├── index.php │ └── static/ └── .env # 也放这里上级,不可被 Web 访问
这样只要 Web 服务 root 是 public/,config/ 和 application/ 就天然无法通过 URL 访问 —— 不需要 .htaccess,也不依赖 PHP 解析。
注意:index.php 里需确认 define('APP_PATH', __DIR__ . '/../application/'); 这类路径是否仍有效,改错会导致白屏。
Apache 下用 .htaccess 补防(仅当无法改 root 时)
如果因历史原因必须把 Web root 设在项目根目录(不推荐),就得靠 .htaccess 拦截。但别写错位置和语法:
-
.htaccess必须放在项目根目录(和application/同级),不是public/下 - 规则要针对每个目录单独写,不能只拦
config/——runtime/日志、common/公共逻辑同样危险 - ThinkPHP 6+ 默认用
app/替代application/,写错目录名等于放行
典型配置(放在项目根目录):
<IfModule mod_rewrite.c>
<Directory "config">
Require all denied
</Directory>
<Directory "runtime">
Require all denied
</Directory>
<Directory "app">
Require all denied
</Directory>
</IfModule>必须确认 Apache 已启用 mod_rewrite,且虚拟主机配置中 AllowOverride All 已开启,否则该文件完全无效。
运行时加 defined() 校验(最后一道保险)
即使路径和权限都对了,万一某天被误配进 public 或遭路径遍历攻击,config.php 文件头加运行时判断能兜底:
在 config/database.php 开头加入:
<?php
if (!defined('THINK_VERSION')) {
exit('Access Denied');
}
// 后续是 return [...];然后确保入口文件 public/index.php 中已定义该常量,例如在 require 框架前加一行:define('THINK_VERSION', '6.1.0');
这个机制对所有 ThinkPHP 版本都生效,但要注意:
- 不能用
IN_SYSTEM这类自定义名,ThinkPHP 自身不定义它,得自己统一维护 - 如果 config 文件是被
include进来的,该判断就起作用;如果是被file_get_contents读取再eval,那这行代码根本不会执行 - 别在
.env里用这种校验 —— 它是纯文本,没 PHP 解析环节
真正难的不是加哪条规则,而是每次部署时检查三件事:Web root 是否指向 public/、敏感目录是否在 root 外、config.php 权限是否为 440 且属主是 root 而非 www。漏掉任意一个,前面所有防护都可能失效。



















