敏感配置必须从 .env 文件读取,禁止硬编码;环境变量需加前缀(如DB_)才能被自动注入;config/app.php 仅存静态配置,避免 getenv() 动态调用导致缓存失效;数据库密码等高危凭据须运行时解密,且密钥与密文必须隔离。

敏感配置必须从 .env 文件读取,不能写死在 config/*.php
ThinkPHP 6+ 默认加载 .env,但仅当配置项显式调用 getenv() 或通过框架自动注入(带前缀如 DB_)时才生效。如果你把 DB_PASSWORD 直接写在 config/database.php 里,哪怕 .env 中有同名变量也不会覆盖——它根本不会被读取。
常见错误现象:git push 后 CI 流水线报错“数据库连接拒绝”,排查发现是测试环境的明文密码被提交到了仓库;或者上线后日志里反复出现 SQLSTATE[HY000] [1045] Access denied,实际是因为 database.php 里的 'password' => 'test123' 没删干净。
-
.env必须放在项目根目录(和think命令同级),且 Web 服务器需禁止直接访问(Nginx 加location ~ /\.env { deny all; }) - 环境变量名要加前缀才能被自动注入:例如
DB_HOST=127.0.0.1→ 在代码中用Config::get('database.hostname')可取到;不加前缀的HOST=...不会进配置 - 所有敏感项(
DB_PASSWORD、APP_SECRET_KEY、AES_KEY_BASE64)都应走.env,而不是靠注释“此处填密码”来提醒自己
config/app.php 里只放非敏感、可缓存的配置
app.php 是运行时高频读取的配置文件,会被编译缓存。一旦里面混入 getenv('DB_PASSWORD') 这类动态调用,会导致缓存失效、CPU 飙升——因为每次请求都要重新 include 并执行该文件。
正确做法是把动态逻辑全移出 config/*.php,只留静态结构。比如:
// config/app.php —— 安全示例
return [
'debug' => (bool) getenv('APP_DEBUG'),
'timezone' => getenv('APP_TIMEZONE') ?: 'Asia/Shanghai',
'default_lang'=> getenv('APP_LANG') ?: 'zh-cn',
];
-
(bool)强制类型转换必须显式写,否则getenv('APP_DEBUG')返回字符串'false'也会被当成true - 所有
getenv()调用都应带默认值(?: 'xxx'),避免返回false导致配置数组键缺失 - 不要在
app.php里做解密操作(如调用openssl_decrypt),那属于启动早期逻辑,应放在public/index.php开头
数据库密码等高危凭据必须运行时解密,且失败立即中断
ThinkPHP 不提供凭据自动解密能力。config/database.php 是纯 PHP 数组,你写的任何表达式都会被执行——这正是解密逻辑该出现的地方,但必须满足三个硬性条件:密钥隔离、失败报错、时机正确。
典型错误写法:'password' => \think\facade\Crypt::decrypt(getenv('DB_PASSWORD_CIPHER')) —— Crypt 组件此时未初始化,会抛 Fatal error: Class 'think\facade\Crypt' not found。
- 解密函数必须能被 PSR-4 自动加载,推荐放在
app/common/Security.php,命名空间为app\common\Security - 密钥必须来自系统级环境变量(
putenv()注入或 Dockerenv_file),不能从.env读——否则等于把密文又存回同一份不安全文件 - 解密失败必须
throw new \Exception('DB credential decrypt failed'),不能返回空字符串或静默降级,否则连接错误日志里只有“Connection refused”,根本看不出是解密环节崩了 - IV 必须随密文一起存储(如 base64 编码后前 24 字符是 IV),绝不能硬编码或复用
加密配置文件(如 config.php.enc)只能用于离线场景,不能替代环境变量
如果你真需要把整个配置文件加密(比如交付给客户时防止查看结构),可以手动在 public/index.php 开头解密并注入 Config,但这不是常规部署方式,且带来额外风险点。
容易踩的坑:file_get_contents(__DIR__.'/../config/config.php.enc') 的路径在不同部署方式下极易出错(symlink、Docker volume、CI 构建目录偏移);openssl_decrypt 失败时返回 false,若没检查就直接 include 'data://...',会导致 ParseError。
- 该方案只适用于一次性交付或私有化部署,SaaS 或云环境一律用环境变量 +
.env - 解密密钥必须由 KMS 或 Vault 管理,不能写死在代码里,也不能用
config('app.aes_key')—— 此时 Config 组件还没加载 - 如果用了 Swoole/Workerman,解密逻辑必须在 Worker 启动前完成,否则每个进程重复解密浪费 CPU
- runtime 目录必须可写,否则配置缓存无法生成,每次请求都重新解密,性能断崖式下跌
加密配置的核心约束其实就一条:**密钥和密文永远不能出现在同一平面**。放在 .env 里算密文,密钥就得是系统环境变量;存在文件里算密文,密钥就得走 KMS;写进数据库算密文,密钥就得存在另一套权限更严的 DB 实例里。混淆这两层,再复杂的加密也形同虚设。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











