SQL注入防御预编译,CMS程序代码改造

SQL注入防御预编译,CMS程序代码改造,核心就一句话:把用户输入和SQL语句结构分离,让数据库先编译再传参。
本文面向零基础站长,从环境确认到代码替换,再到最终验证,一步步带你完成改造,最终让CMS不再拼接用户输入到SQL里。

改造前先确认环境和CMS入口

开始动手前,先确认服务器上的PHP版本和数据库扩展。
预编译依赖PDO或MySQLi,老版本CMS可能还在用mysql_*函数,那套已经废弃且无法防注入。

登录服务器,执行:

php -v
php -m | grep -i pdo
php -m | grep -i mysqli

预期结果是PHP版本在7.0以上,且输出中包含pdo_mysql和mysqli。
如果没有,通过宝塔面板“软件商店”安装对应PHP扩展,或执行apt install php-mysql(Debian/Ubuntu)。

接着定位CMS的数据库操作文件。
常见CMS会把SQL查询封装在/include/db.class.php、/system/database.php或/libs/db.php。
用grep搜索拼接特征:

grep -rn "SELECT.*\$" /www/wwwroot/你的CMS目录 --include="*.php" | head -20
grep -rn "mysql_query\|mysqli_query" /www/wwwroot/你的CMS目录 --include="*.php" | head -20

如果结果里大量出现$sql = "SELECT * FROM users WHERE id = $id"这种写法,说明改造点就在这些文件。

将拼接SQL替换为PDO预编译

改造的核心动作是:用PDO的prepare()和execute()替代直接拼接。
下面以用户登录查询为例。

改造前典型代码:

$username = $_POST['username'];
$sql = "SELECT * FROM admin WHERE username = '$username'";
$result = mysql_query($sql);

改造后使用PDO:

$pdo = new PDO('mysql:host=127.0.0.1;dbname=cms;charset=utf8mb4', 'dbuser', 'dbpass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('SELECT * FROM admin WHERE username = :username');
$stmt->execute([':username' => $_POST['username']]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);

注意:占位符用:username或?,参数通过execute()传入,绝不把变量写进SQL字符串。
如果CMS已有数据库连接类,优先改造该类中的query()方法,让它统一走预处理,这样改动面最小。

对于WHERE id IN (1,2,3)这类动态数量条件,不能直接绑一个数组,需要根据数组长度生成对应数量的占位符:

$ids = [1, 2, 3];
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT * FROM news WHERE id IN ($placeholders)");
$stmt->execute($ids);

改造中容易踩的坑

第一,表名和字段名不能用占位符。
预编译只保护值,不保护SQL结构。
如果排序字段来自用户输入,必须用白名单校验:

$allowed = ['id', 'title', 'addtime'];
$order = in_array($_GET['order'], $allowed) ? $_GET['order'] : 'id';
$sql = "SELECT * FROM news ORDER BY $order";

第二,LIKE查询要处理好通配符。
不能写成LIKE '%:keyword%',正确做法是:

$stmt = $pdo->prepare('SELECT * FROM news WHERE title LIKE :kw');
$stmt->execute([':kw' => '%' . $keyword . '%']);

第三,老CMS可能同时存在多处数据库连接。
改造完一个文件不代表全部安全,需要全局搜索mysql_query和字符串拼接,逐文件替换。

第四,开启PHP错误显示会暴露路径。
改造期间建议在php.ini中设置display_errors = Off,避免报错信息被利用。

验证改造效果是否到位

改造完成后,用两种方式验证。

手工测试:在原来存在注入的输入框(如搜索框、文章ID参数)提交1' AND SLEEP(5)--,如果页面响应时间没有明显延迟,说明预编译生效。

日志检查:在MySQL中开启通用查询日志,观察实际执行的SQL是否还包含用户输入的原样拼接。

SET GLOBAL general_log = 'ON';
SHOW VARIABLES LIKE 'general_log_file';

查看日志文件,如果看到SELECT * FROM admin WHERE username = 'admin'这种带引号的值,且值来自参数绑定,说明改造正确。

也可以使用sqlmap对关键参数做一次扫描,确认不再报注入漏洞。

常见疑问

问:预编译能防御所有SQL注入吗?
不能。它防御的是值拼接类注入。如果SQL结构本身由用户输入决定(如表名、排序方向),仍需白名单校验。

问:CMS用了ORM框架还需要改造吗?
主流ORM默认使用参数绑定,但手写原生SQL的地方仍需检查。建议搜索项目中所有query()或execute()调用,确认参数是否绑定。

问:改造后网站变慢怎么办?
预编译通常比拼接SQL更快,因为数据库可以复用执行计划。如果感觉变慢,检查是否每次请求都新建PDO连接,建议复用连接或使用连接池。

改造完成后,建议保留一份改造前的代码备份,并在测试环境验证通过后再上线。
SQL注入防御预编译不是一次性的工作,后续新增功能也要坚持参数绑定,才能真正守住CMS的安全底线。

分享到:
上一篇
PHP上传临时目录配置,大文件上传优化
下一篇
XSS防御CSP策略,Nginx配置内容安全策略
1
系统公告

泽御云中秋国庆双节活动上线:新购8折,拼团3.99元起

尊敬的用户:
泽御云“月满中秋·礼贺国庆”双节活动现已开启,活动时间为2026年9月23日至10月10日。 活动期间可享以下福利:
1. 常规云服务器新购使用优惠码“泽御中秋国庆同乐”,符合条件的订单享8折优惠。
2. 香港精品云服务器5人拼团低至3.99元,部分4核4G套餐3人拼团年付388元,续费同价。
3. 新用户购买年付云服务器,符合活动规则可赠送2个月使用时长。
4. 老用户续费季度赠15天,续费年度赠2个月;活动期间升级配置免收配置迁移手续费。
5. 推荐好友成功下单,符合条件的推荐人可获赠7天服务器使用时长。
6. 活动期间享宕机补偿标准翻倍、简单网站迁移协助及技术工单优先处理权益。
温馨提示:优惠码不适用于拼团套餐、活动轻量产品、年付订单及续费订单;拼团套餐为独立特价活动,不与赠时类福利叠加。赠送时长不可折现、退款或跨账户转移,具体规则以活动页面说明为准。
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意