给一个线上的 ThinkPHP 项目做了一轮安全检查,发现了 7 个安全隐患。好消息是每个都不难修,有的甚至就改一行配置的事。
1. 数据库 debug 模式没关
配置文件里:
'debug' => true,线上开着 debug 模式,SQL 报错时会把完整的 SQL 语句、数据库表名、字段名全部暴露给用户。攻击者拿到这些信息就能针对性地搞你。
修复:改成 false,一个单词的事。
2. 全局输入过滤没开
ThinkPHP 的 app.php 里有个 default_filter 配置:
'default_filter' => '',空的,意味着所有用户输入都不经过任何过滤就进来了。XSS 攻击随便搞。
修复:
'default_filter' => 'htmlspecialchars,strip_tags',htmlspecialchars 转义 HTML 特殊字符,strip_tags 去掉 HTML 标签。两层防护。
3. 系统配置每次都查数据库
项目里有个 get_system_val() 函数,读系统配置值,每调用一次就查一次数据库。一个页面调用十几次,就是十几次重复查询。
虽然这个更像是性能问题,但频繁的数据库查询在高并发下也是安全风险(慢查询导致服务不可用)。
修复:加 static 变量缓存,同一请求内不重复查。
function get_system_val($code){
static $cache = [];
if (!isset($cache[$code])) {
$cache[$code] = db('system_config')->where('code', $code)->value('val');
}
return $cache[$code];
}4. 密码规则太弱
原来的密码验证:
preg_match('/[0-9a-zA-Z]{6,20}/', $pwd)这个正则的问题:111111 能过,aaaaaa 也能过。没有要求必须同时包含字母和数字。
修复:分开检查,必须同时包含字母和数字:
if (!preg_match('/[a-zA-Z]/', $pwd)) return false;
if (!preg_match('/[0-9]/', $pwd)) return false;5. 重名检查没排除已删除的记录
项目有个 son_repeat() 函数检查同级下是否有重名。但它没有加 is_del=0 的条件,导致已经软删除的记录也会被当成重复。
用户的感受就是:明明删掉了一个分类,再新建同名的时候提示「已存在」。
修复:加一行条件:
$where[] = ['is_del', '=', 0];6. 自动部署脚本裸奔
项目根目录有个 deploy.php,接收 Gitee WebHook 触发自动部署。虽然有密码验证,但没有 IP 限制和频率限制。
如果密码泄露,任何人都能触发部署,往服务器上拉代码。
修复:
- 加 IP 白名单,只允许 Gitee 服务器 IP
- 加频率限制,60 秒内只允许一次
- 记录请求来源 IP 到日志
7. 后台入口没绑定模块
admin.php 是后台入口,但没有绑定 admin 模块:
Container::get('app')->run()->send();理论上可以通过 admin.php 访问到 index 模块的内容,虽然有权限验证挡着,但多一层防护总没坏处。
修复:
Container::get('app')->bind('admin')->run()->send();总结
安全加固不一定要搞得很复杂,很多时候就是:
- 关掉不该开的东西(debug 模式)
- 打开该开的东西(输入过滤)
- 加上缺失的检查(权限、白名单、频率限制)
- 修正不严谨的逻辑(密码规则、重名检查)
这 7 个修复加起来改动不超过 50 行代码,但安全性提升了一个档次。如果你也在维护 PHP 项目,建议对照着检查一遍。
评论
暂无评论