DedeCMS 会员中心的 /member/mtypes.php 在 2014 年被披露存在 SQL 注入漏洞——攻击者通过分类管理表单的 mtypename[id] 数组键名注入恶意 SQL,能直接读取/修改数据库内容(含管理员密码哈希)。这是 DedeCMS 历史上影响最广的漏洞之一,至今很多老站没打补丁仍在裸奔。
这一篇把这个漏洞讲透:根因(PHP 数组键名直接拼到 SQL 里)、原帖给的补丁完整解析、为什么 intval() 是关键、补丁后还有哪些 SQL 注入点、如何系统化扫描整套 DedeCMS 的注入风险、与 WAF / fail2ban 的协同防御、2026 年还在用 DedeCMS 的安全建议。
一、漏洞原理:数组键名 SQL 注入
原始漏洞代码(/member/mtypes.php):
foreach ($mtypename as $id => $name) {
$name = HtmlReplace($name); // ✅ 对 $name 做了过滤
$query = "UPDATE `#@__mtypes` SET mtypename='$name' WHERE mtypeid='$id' AND mid='$cfg_ml->M_ID'";
// ↑↑↑↑
// $id 没过滤!
$dsql->ExecuteNoneQuery($query);
}开发者过滤了数组的 值($name)但忘了过滤 键($id)。PHP 数组的键名可以是任意字符串——攻击者构造一个表单:
<form method="POST" action="/member/mtypes.php?dopost=save">
<input name="mtypename[1' OR '1'='1]" value="payload" />
</form>提交后 PHP 解析得到 $mtypename = ["1' OR '1'='1" => "payload"]。foreach 循环时 $id = "1' OR '1'='1",直接拼到 SQL:
UPDATE `dede_mtypes` SET mtypename='payload' WHERE mtypeid='1' OR '1'='1' AND mid='123'WHERE 条件被打穿——所有 mtypes 行都被改成 mtypename='payload'。更高级的攻击者能用 UNION SELECT ... 读取其它表(dede_admin 拿管理员密码哈希)。
二、补丁的关键改动
原帖给的补丁就一行关键改动:
foreach ($mtypename as $id => $name) {
$name = HtmlReplace($name);
$id = intval($id); // ← 关键:把 $id 强制转成整数
$query = "UPDATE `#@__mtypes` SET mtypename='$name' WHERE mtypeid='$id' AND mid='$cfg_ml->M_ID'";
$dsql->ExecuteNoneQuery($query);
}intval("1' OR '1'='1") 会得到 1(PHP 的 intval 解析到第一个非数字字符就停)。攻击 payload 被中和。
2.1 为什么 intval 比 HtmlReplace 强
| 过滤方式 | 能防 SQL 注入吗 | 能防 XSS 吗 |
|---|---|---|
HtmlReplace(DedeCMS 内置) | 不可靠 | 可(转义 HTML 实体) |
addslashes | 不可靠(GBK 字符集下能绕过) | 不可 |
mysqli_real_escape_string | 对引号内场景可靠 | 不可 |
intval | 对纯整数场景绝对可靠 | 不可(但整数 ID 不参与 HTML 输出) |
| 预处理语句(PDO / mysqli prepare) | 绝对可靠 | 不可 |
对 ID 这种"应该是整数"的字段,intval() 是最稳的——任何非数字内容直接变 0 或截断到第一个数字。预处理语句更通用但 DedeCMS 老代码大量用字符串拼接 SQL,临时性补丁里 intval 性价比最高。
三、补丁还不够:mtypes.php 里的其它注入点
原帖只补了 UPDATE 这一处,但同文件里还有 DELETE 操作 $delids 也存在风险点:
$delids = '0';
$mtypeidarr = array_filter($mtypeidarr, 'is_numeric');
foreach($mtypeidarr as $delid) {
$delids .= ','.$delid;
}
$query = "DELETE FROM `#@__mtypes` WHERE mtypeid IN ($delids) AND mid='$cfg_ml->M_ID';";这里 array_filter(..., 'is_numeric') 已经过滤了非数字键值。但 is_numeric() 接受 "123e+0" 这种科学计数法,理论上可以 "1,2);DROP TABLE x;--" 这种被 is_numeric 拒掉,所以这里相对安全。但严谨做法是每个 $delid 也 intval 一遍:
foreach($mtypeidarr as $delid) {
$delid = intval($delid); // 双保险
$delids .= ',' . $delid;
}四、系统化排查 DedeCMS 整套代码的注入风险
mtypes.php 不是孤例——DedeCMS V5.6 / V5.7 / 早期 SP 版本里类似的"数组键名直拼 SQL"漏洞还有十几处。要系统化排查:
4.1 用 grep 扫"foreach + 拼 SQL"模式
cd /www/wwwroot/yoursite.com/
# 找所有 foreach 后面跟 query 拼接的代码
grep -rn "foreach.*as.*=>" --include="*.php" -A 5 | \
grep -B 1 -E "(query|sql).*\\\$" | head -100每条命中行都需要人工审查:"键名是否被过滤?值是否被过滤?"。
4.2 重点排查路径
/member/— 会员中心,前台用户可达;/dede/— 后台,要求登录但 CSRF 可能放大攻击;/include/payment/— 支付回调,外部可触发;/plus/— 插件目录,历史漏洞最多;/api/— 各种 API 入口。
4.3 已知的 DedeCMS 高危 CVE
| CVE | 位置 | 类型 | 受影响版本 |
|---|---|---|---|
| CVE-2018-9134 | /member/mtypes.php | SQL 注入(本文) | V5.7 SP1 之前 |
| CVE-2018-7700 | /uploads/ | 任意文件上传 | V5.7 SP1 之前 |
| CVE-2019-8362 | /dede/file_manage_view.php | RCE | V5.7 SP2 之前 |
| CVE-2020-25008 | /include/dialog/ | 任意文件读取 | V5.8.1 之前 |
| CVE-2022-23047 | /dede/article_string_mix.php | SQL 注入 | V5.7.106 之前 |
5 条只是"明显的"——实际累计高危漏洞超过 50 条。建议:① 升级到最新社区分叉 DedeBIZ;② 定期跑 D 盾、河马等漏洞扫描器;③ 上 WAF 兜底。
五、SQL 注入的攻击面与影响
5.1 攻击者能做什么
- 读取任意表:
UNION SELECT password FROM dede_admin拿管理员密码; - 修改任意数据:
UPDATE dede_admin SET pwd='伪造哈希' WHERE id=1; - 删除整表:
DROP TABLE dede_archives; - 写文件:MySQL 的
SELECT ... INTO OUTFILE '/path/shell.php'(需要 FILE 权限),写 webshell; - 读文件:MySQL 的
LOAD_FILE('/etc/passwd')(需要 FILE 权限); - RCE:组合注入 + outfile 写入 PHP webshell,再访问执行任意命令。
5.2 防御纵深
即使 PHP 代码有注入,也能通过其它层次缓解:
- 数据库账号最小权限:DedeCMS 数据库用户只给 SELECT/INSERT/UPDATE/DELETE,不给 FILE / DROP,注入也写不了 webshell;
- WAF:Cloudflare / 阿里云 WAF 自带 SQL 注入特征拦截,能挡 80% 自动化扫描;
- Web 服务器禁止 PHP 在上传目录执行:即使 webshell 写到 /uploads/,Nginx 配置 location /uploads/ 拦 .php 请求;
- fail2ban 拉黑高频 SQL 注入特征请求。
六、用 PDO 预处理语句重构 mtypes.php
临时补丁是 intval。彻底修复用 PDO 预处理,永久无忧:
// 假设把 DedeCMS 的 $dsql 替换成 PDO 实例
$pdo = new PDO('mysql:host=localhost;dbname=dede;charset=utf8mb4', $user, $pass);
foreach ($mtypename as $id => $name) {
$stmt = $pdo->prepare("UPDATE dede_mtypes SET mtypename = :name WHERE mtypeid = :id AND mid = :mid");
$stmt->execute([
':name' => HtmlReplace($name),
':id' => (int)$id, // 显式转 int
':mid' => (int)$cfg_ml->M_ID,
]);
}预处理语句的好处:参数永远不会被解释为 SQL 关键字,无论攻击者怎么构造 payload 都不会破坏 SQL 结构。性能上 PDO 预处理还能让数据库缓存执行计划,比拼字符串 SQL 还略快。
七、补丁部署的注意事项
7.1 升级前务必备份
cp /www/wwwroot/yoursite.com/member/mtypes.php /backup/mtypes.php.$(date +%Y%m%d)万一打补丁打错或冲突,能 1 分钟回滚。
7.2 同时清模板缓存
修改 PHP 文件后清 data/tplcache/,避免老缓存还在用旧版逻辑。
7.3 验证补丁生效
用 sqlmap 测试一下:
sqlmap -u "https://yoursite.com/member/mtypes.php?dopost=save" \
--data="mtypename[1*]=test&cfg_ml=1" \
--cookie="DedeUserID=xx; DedeLoginTime=xx" \
--level=3 --risk=2未打补丁的版本会被 sqlmap 立刻发现注入;打了补丁的应报"no injection point found"。
八、长期建议:彻底升级或迁移
修一处漏洞只是补一个小坑。彻底解决:
- 升级到 DedeBIZ V6.x:社区分叉版,活跃维护,已合并大部分历史 CVE 补丁;
- 切到 WordPress + 迁移内容:WordPress 的核心代码经过更严格审计,安全生态更成熟;
- 用静态站生成器:Hexo / Hugo / Jekyll 等,没有动态后端就没有 SQL 注入面。
2026 年仍然新建 DedeCMS 站风险远大于收益——历史漏洞之多、社区维护之弱,每个上线日都是赌博。
九、运行时防御:上 WAF
即使代码有补丁,上 WAF 是额外的兜底。Cloudflare 免费版自带 OWASP 核心规则集,能拦绝大多数自动化扫描器:
Cloudflare WAF Rule (Free Plan):
- SQL Injection (SQLi) protection: ON
- Cross-Site Scripting (XSS) protection: ON
- File Inclusion (LFI/RFI): ON阿里云 WAF / 腾讯云 WAF 国内方案类似。WAF 不是替代代码补丁——是补丁的补充。代码必须修对,WAF 是兜底。
十、监控与告警
装好 WAF 后再加监控:
- fail2ban:扫 access.log 里高频的 ?dopost=save、SQL 注入特征字符串,发现一个 IP 反复试探就拉黑;
- Web 服务器日志告警:HTTP 500 / 502 错误突增(可能是注入后 SQL 报错);
- 数据库监控:突然出现的大量 UPDATE / DELETE 事件,流量异常;
- 邮件告警:管理员账号被改密码、登录 IP 异常都触发邮件。
十一、应急响应流程(如果发现已被入侵)
万一晚一步——发现站点已经被注入或挂马,按以下顺序处理:
- 立刻断网:暂停 Nginx / Apache 服务,避免攻击者继续操作;
- 全量备份当前状态:包括所有 PHP 文件、数据库、access.log、error.log——证据保全;
- 修改所有数据库密码:DedeCMS 数据库账号、管理员账号哈希;
- 查 access.log 找入侵时间窗:搜可疑 payload(含
'、UNION、SELECT的 POST 请求),确定攻击起点; - 对比文件 mtime:
find . -name "*.php" -newer /var/log/messages -mtime -7找最近 7 天修改过的 PHP 文件,重点排查是否被加 webshell; - 清理 webshell:常见后门函数
eval()/assert()/preg_replace 加 /e 修饰符,grep 整套代码找; - 修补漏洞:本文 mtypes.php 补丁 + 升级到 DedeBIZ;
- 重置所有用户密码:admin、会员都强制重置;
- 恢复服务:开 Nginx,监控 24 小时;
- 事后复盘:写故障报告,归档攻击轨迹与防御加固清单。
11.1 找 webshell 的 grep 命令速查
# 找含 eval 的 PHP 文件
grep -rln "eval(" --include="*.php" /www/wwwroot/yoursite.com/
# 找 base64 编码的 PHP 字符串(webshell 常用伪装)
grep -rln "base64_decode" --include="*.php" /www/wwwroot/yoursite.com/
# 找含 assert 的(assert 也能执行 PHP)
grep -rln "assert(" --include="*.php" /www/wwwroot/yoursite.com/
# 找文件包含 + 用户输入(LFI/RFI)
grep -rEn "(include|require)(_once)?[\s(]+\\\$_(GET|POST|REQUEST)" --include="*.php" /www/wwwroot/yoursite.com/每条命中的文件都要人工审查——是合法用法还是 webshell。
常见问题解答
怎么知道我的站有没有被攻击过?
三个排查点:① 看 web 服务器 access.log 找 mtypename[ + 引号 / OR / UNION 等可疑 payload;② 看 dede_admin 表有没有意外新增的管理员账号;③ 看 dede_mtypes 表的 mtypename 字段有没有奇怪的值(被 UPDATE 篡改痕迹)。fail2ban 日志和 WAF 告警也是入口。
HtmlReplace 真的不能防 SQL 注入吗?
不能。HtmlReplace 是 DedeCMS 自带的 HTML 转义函数(把 < 变 < 之类),针对 XSS 设计的。对 SQL 没有任何保护——SQL 不解析 HTML 实体。要防 SQL 注入要用 intval / addslashes / 预处理语句。
DedeCMS 已经停止维护,补丁从哪里拿?
三个来源:① DedeBIZ 社区分叉(https://www.dedebiz.com/)已经合并了大部分历史 CVE,下载最新版即可;② D 盾 / 河马等国产 PHP 漏洞扫描器自带 DedeCMS 补丁库;③ 自己根据 CVE 公告手动改代码(本文方式)。生产环境推荐方案 ①。
升级 DedeBIZ 会影响现有数据吗?
不会。DedeBIZ 与 DedeCMS V5.7 数据库 schema 兼容——只是替换 PHP 文件,数据库不动。但升级前必须备份所有 PHP 文件 + 数据库,万一新版本与你的自定义模板/插件冲突,能回滚。
会员中心是否要彻底关闭?
看业务。如果你的站不需要会员(纯展示型企业站),关闭整个 /member/ 目录是最安全的——直接 Nginx deny all 这个 location,所有相关漏洞瞬间消失。如果业务需要会员,要么升级 DedeBIZ,要么自己审计代码 + 加 WAF。
修补丁后还要不要清 Web 服务器缓存?
看你用的缓存层。① OPcache(PHP 自带 opcode 缓存)会缓存 PHP 文件解析后的字节码,文件改动后默认监测 mtime 自动失效,但保险起见可 opcache_reset() 或重启 PHP-FPM;② 静态页面缓存(Cloudflare / 阿里云 CDN)跟 PHP 修改无关,但如果你的攻击页 URL 被 CDN 缓存了攻击响应,要清 CDN 缓存。
WAF 拦截的请求会影响真实用户吗?
会,但概率极低。WAF 偶尔会误报(比如用户输入的关键词恰好像 SQL)。Cloudflare WAF 有"挑战模式"——可疑请求弹验证码而不是直接 403,平衡安全与用户体验。生产建议上线前用 Log Mode 跑 1 周看告警准确率,再切到 Block Mode。
预处理语句会比拼字符串 SQL 慢吗?
不会。① 数据库会缓存预处理执行计划,二次执行更快;② PHP PDO 的预处理本质就是给参数加引号 + 转义,开销几乎为零;③ 在大查询里两者性能差异在毫秒级。预处理语句永远是更优选择,没有不用的理由。
DedeCMS V5.7 SP3 / SP4 修了多少漏洞?
SP2 之后官方维护放缓,SP3 / SP4 主要是 PHP 8 兼容修复 + 少量 CVE 补丁。绝大部分高危历史漏洞要靠社区分叉(DedeBIZ)维护。如果你站点版本老于 V5.7 SP2,立刻升级是头等优先。
2026 年还能新装 DedeCMS 吗?
从技术上能,但非常不推荐。原因:① 大量历史漏洞累积;② 官方停更,没有持续安全补丁;③ PHP 8 / MySQL 8 兼容性差;④ 现代 SEO 友好度不如 WordPress;⑤ 模板/插件生态萎缩。新建项目用 WordPress / Hexo / 自研 + 现代框架的成本远低于"用 DedeCMS + 持续打补丁"。