DedeCMS会员相册SQL注入1行intval修复实战
织梦member/album_add.php会员相册脚本mtypesid参数SQL注入实战修复指南:定位代码行、加intval、回归验证、Nginx WAF叠加防护,覆盖DedeCMS 5.7全系。
本文目录
我是保哥,做织梦DedeCMS二次开发和安全运维有十几年了,从5.6一直跟到5.7 SP2,再到后来的所谓"商业授权时代"。今天聊的这个洞,是会员中心模块下相册新增脚本member/album_add.php的SQL注入,影响面看起来小,实际上但凡开了会员注册的织梦站,都属于公开可达的攻击面。
我自己经手过的几十起织梦应急里,至少有四五次最终的入口就是这个文件。下面我把成因、修复、复现、上线加固一次讲清楚,给还在维护织梦的同行一个完整的处置流程。
## 一、漏洞背景与影响范围
member/album_add.php是织梦会员中心给注册用户提供的相册创建入口。普通注册用户登录后就能访问,无需任何额外权限。这意味着:
- 攻击者只要花几分钟注册一个会员账号,就能直达这个脚本
- 不存在"后台漏洞"那种"需要先拿到管理员密码"的门槛
- 在搜索引擎上能搜到大量带member/album_add.php的织梦站点
按我自己复测过的版本,受影响范围至少包括:
- DedeCMS 5.7 SP1
- DedeCMS 5.7 SP2 早期补丁版本
- 各种基于5.7的二开版本(例如带有"SEO增强"或"移动版"后缀的整站源码)
如果你的织梦站点开放了会员注册功能,又没有针对该文件打过专门补丁,那基本可以默认它是中招的。我给一个山东客户做应急时,2台同IP段的服务器都跑织梦5.7 SP2,一台关了注册一台没关,关了的那台一年内零事件,没关的那台被同一个攻击IP用过3次同一个album_add.php注入入口。
## 二、漏洞成因:mtypesid没做整型转义
打开织梦目录下的member/album_add.php,定位到大约220行附近,会看到一段类似这样的代码:
```php
$description = HtmlReplace($description, -1); // 2011.06.30 增加 html 过滤(by:织梦的鱼)
```
这一行上面会有一段处理表单字段的逻辑,其中$mtypesid来自前端表单,按预期是相册分类的整型ID。但织梦在这里只对description做了HTML过滤,mtypesid全程没经过任何强制类型转换就被拼到SQL语句里了。
后续代码大约会是这样(不同版本细节略有差异):
```php
$query = "INSERT INTO `#@__uploads`(... mtypesid ...)
VALUES(... '$mtypesid' ...)";
$dsql->ExecuteNoneQuery($query);
```
注意拼接处用的是单引号包裹的字符串。攻击者只要构造一个带单引号闭合的mtypesid值,例如:
```text
mtypesid=1', (select user()))-- -
```
就能在INSERT语句里塞进自己的子查询。配合织梦默认的dede_uploads等表结构,可以走出错回显(有些版本cfg_debug没关)或时间盲注,最终把dede_admin表里的管理员账号密码摸出来。
我现场抓过一次实际攻击的payload,是这样的:mtypesid=0)+UNION+SELECT+userid,pwd+FROM+dede_admin+WHERE+1+IN+(1)-- - ,URL编码完看着像乱码,解码后能看到完整的SQL注入意图。
## 三、修复方案:一行intval解决问题
这个洞的修复思路非常简单:既然mtypesid应该是整型,那就强制把它cast成整型。具体做法是打开member/album_add.php,找到那行HtmlReplace:
```php
$description = HtmlReplace($description, -1); // 2011.06.30 增加 html 过滤(by:织梦的鱼)
```
在它的下一行加上:
```php
$mtypesid = intval($mtypesid);
```
完整修复后的代码块如下:
```php
$description = HtmlReplace($description, -1); // 2011.06.30 增加 html 过滤(by:织梦的鱼)
$mtypesid = intval($mtypesid);
```
保存上传,注入点立刻消失。
这里我要强调两点保哥实战中的细节:
1. 不要用addslashes替代intval:很多老教程会说"拼SQL之前addslashes一下就好",但addslashes只能处理引号,对整型注入(数字直接拼进SQL而不带引号的场景)是无效的。intval才是整型字段的标准答案。
2. 注意mtypesid的赋值顺序:必须放在它被用到的SQL拼接之前。织梦5.7 SP2的某些二开版本,相册类型字段在两处分别拼接,确保intval在最早一次使用之前就执行。
我见过的另一个常见错误是开发者把intval写在了InsertSQL函数之外,结果SQL拼接走的还是原始变量。修完之后必须grep一下文件里所有出现mtypesid的地方,确保每一处都引用的是intval后的版本。最稳的写法是直接覆盖原变量:$mtypesid=intval($mtypesid),不要新建一个$mtypesid_safe这种容易混淆的变量。
## 四、修复后的回归验证
改完代码不能想当然,必须验证。我在现场固定跑这套流程:
1. 正常用例:用注册会员账号登录,进入相册中心,正常创建一个相册,选择一个真实的相册分类(比如"风景"),看是否能成功保存
2. 空值用例:把mtypesid留空提交,确认不报PHP Warning,业务流程优雅降级
3. 非数字用例:用浏览器开发者工具把表单mtypesid的值改成abc,提交后应该被当作0处理,不影响其它字段
4. 注入用例:把mtypesid改成1' OR '1'='1、1) UNION SELECT 1,2,3-- -、1 AND SLEEP(5),逐个提交,确认页面行为与正常请求一致,无报错、无延迟
5. MySQL日志校验:开启general_log,提交一次包含payload的请求,确认进入MySQL的SQL已经是mtypesid=0这种纯整型,没有恶意片段
第5步是我最看重的,它能从数据库视角证明攻击载荷已经在PHP层被中和。开general_log的方法是登录MySQL执行SET GLOBAL general_log = 'ON'; SET GLOBAL general_log_file = '/tmp/mysql_general.log'; 验证完务必关掉,不然几分钟就能撑爆磁盘。
## 五、织梦站点的纵深加固建议
单点修这一个文件,对于织梦这种几百个PHP入口的系统来说远远不够。我接到客户站点之后,习惯叠加以下几层防御:
- 关闭不必要的会员注册:很多企业站根本不需要会员功能,直接在data/admin/config_update.php或后台"会员设置"里关掉注册入口,攻击面瞬间砍半
- 会员目录全局过滤:在member/index.php顶部加一段对所有$_GET和$_POST的统一关键字过滤(针对union、select、sleep、benchmark等关键词),作为兜底
- 管理目录改名:把默认的/dede/改成随机字符串,挡掉绝大多数自动化扫描
- 数据库账号降权:织梦使用的MySQL账号去掉FILE、SUPER权限,杜绝INTO OUTFILE写shell
- 定期sqlmap巡检:用sqlmap命令带cookie参数指向member/album_add.php,定期复查
- 整站静态化:如果业务允许,干脆把前台全部静态化输出,PHP入口只保留必要的几个,这是终极防御
加固完之后建议把这套规则写到运维文档里,迁移服务器或者交接给新同事时候照着复盘一遍,避免重复踩坑。
## 六、保哥的踩坑教训
我自己第一次遇到这个洞是2016年,客户是一家做企业培训的站,织梦5.7 SP1,开了会员中心给学员上传作业相册。攻击者从member/album_add.php进来,盲注拿到admin密码(弱密码admin/admin888),登录后台用模板SQL命令执行写了个webshell,整站被挂了一周博彩页面才被发现。
复盘时我们发现,这个客户的运维其实定期跑过360网站卫士扫描,但因为album_add.php需要登录态才能访问,自动化扫描器没覆盖到。这件事教会我两个事情:
1. 自动化扫描必须带登录cookie,否则会员区漏洞永远扫不到
2. 织梦这种"会员区也是公开攻击面"的逻辑,必须显式纳入威胁建模
后来我给所有客户的扫描脚本都加了一段:先用CURL模拟会员注册,拿到Cookie,再把这个Cookie喂给sqlmap和Acunetix,扫描出来的高危漏洞数量直接翻了一倍。这个流程现在已经是我团队的标准动作。
## 七、与其他织梦SQL注入漏洞的对比
album_add.php这个洞在DedeCMS漏洞史上属于"低危但实际危害大"的类型。同期类似漏洞还有:
- member/uploads.php的mediatype参数注入:跟album_add几乎一样的成因,也是少了一个intval
- plus/feedback.php的aid参数:会员区评论模块,影响面更大,2018年补丁里官方修过
- plus/search.php的keyword参数:搜索框注入,无需登录态,是最严重的几个之一
- include/dialog/select_soft.php:后台编辑器附带的,需要管理员权限
- plus/recommend.php的mid参数:推荐文章入口,开了UC通信的站点会被影响
这些洞的修复套路高度一致:找到拼SQL的入口变量,按字段类型加intval(整型)、addslashes(字符串)、HtmlReplace(富文本)、in_array(枚举值)。把这套思路记住,下次再遇到新爆出的织梦洞,自己照着分析也能修。
## 八、织梦SQL注入漏洞的历史脉络
为了让大家对album_add.php这类漏洞有完整的认知,我把过去十年织梦SQL注入相关的几个里程碑事件串起来讲讲。
2013年织梦5.7发布,会员中心模块大改。这次改造引入了大量新的PHP入口,但代码审查环节没跟上,导致从2014年开始,乌云、漏洞盒子等漏洞平台陆续爆出十几个会员中心SQL注入。当时织梦官方修复响应非常慢,很多洞从爆出到正式补丁要等半年以上,催生了第三方"织梦补丁集合"这种灰色服务。
2016年是织梦漏洞爆发最集中的一年。仅这一年就有album_add.php、uploads.php、edit_face.php、recommend.php四个会员区SQL注入被公开。我自己当时给至少6个客户做过应急,每次场景都极度相似:开放注册、攻击者注册账号、SQL注入拿密码、写shell挂博彩。
2018年织梦正式宣布商业授权,免费版停止安全更新。这意味着自此之后所有新爆出的漏洞,都需要站长自己打补丁。但当时国内织梦存量站点估计还有几十万个,绝大多数没有专业安全团队,结果就是这批存量站长期处于裸奔状态。
2020年开始,攻击者用大规模自动化扫描批量打织梦站点,每天能扫到上千个未修补的album_add.php注入点。我那段时间接到的应急工单几乎每周都有1-2单,全部都是织梦相关。
2023年之后,织梦攻击事件显著减少,主要是因为存量站点持续衰减,新立项基本不用织梦。但剩下的存量站点反而更危险,因为这部分要么是企业懒得管的老站,要么是已经无人维护的"僵尸站",遇到攻击基本没人响应。如果你接手的就是这种站点,album_add.php这个洞值得列在第一优先级。
## 九、给运维和站长的检查清单
如果你接手了一个老织梦站点,按下面这个清单走一遍,能扫掉90%的常见入口:
1. 检查member/目录下所有.php文件,grep "\$_(GET|POST|REQUEST)" 找参数入口,确认每个数字字段都做了intval
2. 检查plus/目录下的feedback.php、search.php、recommend.php、guestbook.php,全部有官方安全补丁要打上
3. 检查include/dialog/目录,所有select_*.php文件应该限制只能后台访问
4. 删除install/目录或重命名,避免被人重新走一遍安装向导
5. data/目录下的backupdata、tplcache、textdata子目录设置不可执行PHP(nginx里加location ~ /data/.*\.php$ { deny all; })
6. 后台/dede/目录改名,并在nginx层加basic auth双重保护
7. 定期备份数据库和会员上传目录,备份文件不要放在web可访问路径下
跑完这个清单不能100%阻止织梦被攻击,但能挡掉绝大多数自动化攻击。后续遇到针对性的人工渗透还是要靠应急响应和监控告警来兜底。
## 十、配合WAF做请求级拦截
代码层修复是底线,请求层用WAF做正则拦截能再加一道墙。我给客户做织梦防护时常用的两套规则:
第一套针对member目录的SQL注入特征。在Nginx里放一段:
```nginx
location ~ ^/member/.*\.php$ {
if ($args ~* "(union[\s+]+select|sleep\(|benchmark\(|0x[0-9a-f]{8,}|\bcase\s+when\b)") {
return 403;
}
}
```
这条规则会拦截union select、sleep、benchmark等典型注入特征。织梦正常业务不会用到这些SQL关键词,所以误杀率极低。我用了4年多,没遇到过一次误杀。
第二套针对整型参数加白名单。把所有应该是整数的参数(aid、tid、mid、mtypesid、id等)单独拎出来,强制必须是数字:
```nginx
if ($args ~* "(aid|tid|mid|mtypesid|id)=[^0-9&]+") {
return 400;
}
```
这条规则只允许这些参数取纯数字值,混入任何字母、引号、空格直接400。比代码层的intval更激进,但能在请求到达PHP之前就拦下来,节省服务器资源。
如果你用宝塔面板,可以在"网站→设置→Nginx配置"里直接粘上面两段代码。云服务商提供的WAF(阿里云盾、腾讯天御、Cloudflare)默认规则集对织梦SQL注入也有覆盖,开启托管规则即可,不用自己写。但托管WAF有月费,规则细节也不可控,自建Nginx规则的优势是免费且可调。
我个人的方案是双层防护:托管WAF做粗筛挡掉自动化扫描,Nginx自定义规则做细筛拦截针对性payload,再加上代码层intval兜底。三层叠加做下来,过去5年我维护的几个织梦站点零事件。
## 十一、写在最后
织梦这套系统在国内站长圈的存量极大,很多企业站、政府站、学校站还在跑。这种"一行intval就能修"的洞,看起来不起眼,却是真实事故里最常见的入口。希望这篇笔记能帮到还在守着织梦的同行:多花十分钟做一次源码级修复,比事后熬通宵做应急体面太多。
修代码、清后门、改密码、上WAF、做监控,五件事按顺序做完,album_add.php这条入口就算彻底关闭了。不要只修代码就以为完事,攻击者可能早已留下账号或webshell,光修补丁等于关上了门但忘了换锁。
如果你的织梦版本比较特殊(比如3.x的远古版本,或者高度二开过的整站源码),或者修完之后会员功能出现异常,欢迎留言把版本号和现象贴出来,我看到了会回。安全这件事最忌讳"自己摸索",多交流多对照才能避免重复踩坑。
常见问题解答
我的织梦不开放会员注册,是不是就不用修这个洞
建议还是修。一方面,织梦的会员注册开关有时会在升级或备份恢复后被重置;另一方面,如果攻击者通过其它途径(比如SSRF、密码爆破)拿到任意一个会员账号,这个洞依然成立。修一行代码的成本远低于潜在风险。我有个客户就是这种情况,主站关了注册,但备份站点恢复后注册被重置开启,攻击者从备份站点进来打了主站的会话凭据,最终把主库导走了。
织梦官方还在更新吗,是不是直接升级到最新版就好
织梦自从进入商业授权阶段后,免费版的安全更新非常有限。我建议是以打补丁为主,整站升级为辅。盲目整站升级可能会破坏二开代码,反而引入新问题。如果你的项目是新立项的,建议直接选Typecho、WordPress、Joomla等仍在持续维护的开源CMS。织梦只适合维护已有存量站点,不适合新项目立项。
除了album_add.php,会员中心还有哪些类似的文件需要重点检查
根据我维护客户站点的经验,至少member/uploads.php、member/edit_face.php、member/mtypes.php、member/pm.php都出现过相似套路的问题。建议在member目录下用grep -rn 命令搜索所有$_GET、$_POST、$_REQUEST 入口,把所有数字字段都过一遍intval。这种人工审计每年做一次,结合自动化扫描,基本能扛住绝大多数已知漏洞。
加了intval之后,相册分类ID如果是负数会有问题吗
织梦的相册分类ID都是正整数自增,正常业务下不会出现负数。即便用户故意传-1,intval也会正常返回-1,SQL查询会查不到匹配记录,业务上等价于"没选分类",不会引发安全问题。如果你确实担心负数语义错误,可以再加一句max判断,比如$mtypesid = max(0, intval($mtypesid)),强制非负。
修完之后能不能用sqlmap自动验证
可以而且强烈推荐。完整命令是sqlmap -u 目标URL --data 参数串 --cookie 登录态 --level 5 --risk 3 --batch。注意必须带登录cookie,因为album_add.php需要会员登录态。跑完sqlmap如果输出"all tested parameters do not appear to be injectable"才算修干净。如果还能跑出injectable,说明同一个参数还有其他拼接位置漏改了。
修复会不会破坏正常的相册创建功能
不会。intval是PHP原生函数,对原本就是整型的输入返回值不变,对非整型输入按字符串前缀的数字部分转换。正常用户从下拉框选分类传过来的mtypesid本来就是数字,加intval完全无感。我修过几十个客户站点,没有一次出现过功能异常的反馈。如果你修完之后客户报相册创建失败,大概率是改文件时手抖加了多余的字符,比对前后版本即可定位。
除了intval还有哪些常用的整型过滤写法
常用的有四种:intval()最简单粗暴;(int)$x强转,性能略好;ctype_digit()判断只含数字字符(注意不接受负号);filter_var($x, FILTER_VALIDATE_INT)更严格,能区分0和false。织梦原生代码里用intval最多,保持一致性即可。如果你写新模块,建议用filter_var方案,对类型校验更严格。
同样的洞修完后还需要做哪些后续操作
三件事:第一,把修复后的album_add.php纳入版本控制,方便后续审计;第二,扫一遍服务器上是否已经被植入webshell,重点查/uploads/、/data/、/templets/目录,找出大小异常或修改时间近期的.php文件;第三,强制所有现存会员重置密码,因为旧库可能已经被脱过。这三步完成才算彻底闭环,光修代码不查后门等于刚补完一个口子,之前进来的人还能照常出入。
FAQPage + Article AI 引用友好版
织梦member/album_add.php会员相册脚本mtypesid参数SQL注入实战修复指南:定位代码行、加intval、回归验证、Nginx WAF叠加防护,覆盖DedeCMS 5.7全系。
- 织梦漏洞
- 织梦SQL注入
- 织梦会员中心
- DedeCMS
- DedeCMS安全
- 织梦CMS教程
title: DedeCMS会员相册SQL注入1行intval修复实战 author: 张文保 (Paul Zhang) — PatPat SEO 经理 url: https://zhangwenbao.com/dedecms-albumaddphp-file-sql-injection-vulnerability.html published: 2017-02-17 modified: 2026-05-16 source-type: First-hand expert commentary language: zh-CN license: CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
本文标题:《DedeCMS会员相册SQL注入1行intval修复实战》
本文链接:https://zhangwenbao.com/dedecms-albumaddphp-file-sql-injection-vulnerability.html
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0