帝国CMS 信息发布AddNews提交链路SQL防注入机制分析

作者:凡帝网 分类:帝国CMS教程 发布时间:2026-07-24 浏览量:10

以 AddNews.php 提交链路为例,剖析帝国CMS后台表单字段的 SQL 注入防护体系。

一、总体架构

帝国CMS在信息发布(增/改)流程中,采用分层过滤、因字段而异的防护策略:

浏览器表单
    ↓ POST
ecmsinfo.php  ← 直接接收 $_POST,不做处理
    ↓
hinfofun.php::AddNews / EditNews
    ├── DoPostInfoVar($add)      ← 标准字段(标题、属性、分类…)
    └── ReturnAddF($add, ...)    ← 模型自定义字段(newstext、smalltext 等)
                                     最后 addslashes 入库

关键原则:入口页(ecmsinfo.php)只做路由,不负责过滤;所有过滤下沉到业务函数内部。


二、标准字段处理:DoPostInfoVar()

文件:e/class/hinfofun.php:103

function DoPostInfoVar($add){
    global $class_r,$public_r;
    $add['classid']=(int)$add['classid'];
    // ...
}

2.1 数字字段 → (int) 强转

所有整型字段直接用 (int) 强制转换,SQL 注入代码在转换中灰飞烟灭:

字段 处理
classidcheckedistop (int)$add['classid']
isgoodgroupidnewstempid (int)$add['isgood']
firsttitleuserfenclosepl (int)$add['userfen']
ttidonclicktotaldown (int)$add['onclick']
newwfidoldwfiddokey (int)

原理:PHP 的 (int) 遇到 "1; drop table" → 1,遇到纯字母 → 0,不可能注入。

2.2 字符串字段 → 分类多层过滤

// HTML转义 + URL &保留(纯文本场景)
$add['title']      = eDoRepPostComStr($add['title'], 1);
$add['keyboard']   = eDoRepPostComStr(RepPhpAspJspcodeText(...), 1);
$add['titleurl']   = eDoRepPostComStr(RepPhpAspJspcodeText(...), 1);
$add['infotags']   = eDoRepPostComStr(RepPhpAspJspcodeText(...), 1);
$add['titlepic']   = eDoRepPostComStr($add['titlepic'], 1);

// SQL检查 + addslashes(字符串内容)
$add['keyboard']   = hRepPostStr($add['keyboard'], 0);
$add['infotags']   = hRepPostStr($add['infotags'], 1);

// 文件名净化
$add['filename']   = RepFilenameQz($add['filename'], 1);
$add['newspath']   = RepFilenameQz($add['newspath'], 1);
$add['newspath']   = hRepPostStr($add['newspath'], 1);
$add['filename']   = hRepPostStr($add['filename'], 1);

各函数职责:

函数 作用 保留HTML
eDoRepPostComStr ehtmlspecialchars + 可选 & 还原
hRepPostStr ehtmlspecialchars(可选) + CkPostStrChar(SQL关键词拦截) + AddAddsData(addslashes) ❌($ecms=1时转义)
RepPhpAspJspcodeText 删除 PHP/ASP/JSP 代码标签
RepFilenameQz 文件名规范化,去路径遍历

2.3 调用链路图

eDoRepPostComStr($val)
  ├── CkPostStrIsArray($val)    ← 数组直接报错
  └── ehtmlspecialchars($val)   ← HTML实体转义

hRepPostStr($val, $ecms)
  ├── CkPostStrIsArray($val)
  ├── [可选] ehtmlspecialchars($val)
  ├── CkPostStrChar($val)       ← SQL关键词拦截
  └── AddAddsData($val)         ← addslashes

三、自定义字段处理:ReturnAddF()

文件:e/class/functions.php:7002

这才是真正的核心防护——模型里定义的所有自定义字段(编辑器内容、简介、下载地址、图片集等)都在这里处理。

3.1 每条字段值的过滤管线

// 遍历模型定义的所有字段
foreach (explode(',', $emod_r[$modid]['enter']) as $f) {

    // 1. 清除PHP/ASP/JSP代码
    $value = RepPhpAspJspcodeText($add[$f]);

    // 2. 特殊字段类型处理
    if ($f == 'newstime')    $value = to_time($value);
    if ($f == 'morepic')     $value = ReturnMorepicpath(...);
    if ($f == 'downpath')    $value = ReturnDownpath(...);
    if ($f == 'smalltext')   $value = SubSmalltextVal(...);
    if ($f == 'infoip')      $value = egetip();

    // 3. 全角/半角统一
    $value = eDoInfoTbfToQj($value);

    // 4. 字段类型校验(int/float/varchar...)
    $value = hEModCkFieldType($value);

    // 5. 自定义函数钩子
    $value = DoFFun($value);

    // 6. 模板变量处理
    $value = RepTempvarPostStr($value);

    // ★★★★★ 7. 核心:SQL注入过滤 ★★★★★
    $value = hRepPostStr2($value);

    // 8. 如果是 newstext(编辑器内容),额外处理:
    if ($f == 'newstext') {
        $value = addslashes(CopyImg(      ← 远程图片下载 + addslashes
            stripSlashes($value), ...
        ));
    }

    // ★★★★★ 8. 最终入库前再 addslashes ★★★★★
    addslashes($value);
}

3.2 hRepPostStr2 做了什么

function hRepPostStr2($val, $phck=0){
    $val = CkPostStrIsArray($val);     // 防数组注入
    if ($phck==1) CkPostStrCharYh($val); // 括号配对检查
    CkPostStrChar($val);               // SQL关键词拦截
    $val = AddAddsData($val);           // addslashes
    FWClearGetText($val);              // 防火墙
    return $val;
}

与 RepPostStr 的区别:

  RepPostStr hRepPostStr2
HTML转义 (ehtmlspecialchars) ✅ 有 ❌ 没有,保留HTML
SQL关键词拦截 可选 ($ecms=0) ✅ 始终执行
addslashes 可选 ✅ 始终执行
防火墙 可选 ✅ 始终执行
适用场景 纯文本、标题 富文本编辑器内容

这正是为什么 newstext 等编辑器字段能保留 <p> <img> 标签的同时,还能防 SQL 注入。

3.3 newstext 的特殊处理链路

原始POST值
  → RepPhpAspJspcodeText         ← 清除 PHP/ASP/JSP
  → hEModCkFieldType             ← 类型校验
  → hRepPostStr2                 ← SQL过滤 + addslashes(保留HTML标签)
  → addslashes(CopyImg(...))     ← 远程图片下载 + 再addslashes
  → 最终拼入SQL

双重 addslasheshRepPostStr2 内部一次 + 拼 SQL 时一次,形成纵深防御。


四、最终入库 SQL 拼接

以主表 INSERT 为例(hinfofun.php:297):

$empire->updatesql("insert into ".$infotbr['tbname']."(
    id, classid, ttid, onclick, plnum, totaldown,
    newspath, filename, userid, username,
    firsttitle, isgood, ispic, istop,
    isqf, ismember, isurl, truetime, lastdotime,
    havehtml, groupid, userfen, titlefont, titleurl,
    stb, fstb, restb, keyboard
    {$ret_r['fields']}           ← 自定义字段
) values(
    '{$id}','{$add['classid']}','{$add['ttid']}','{$add['onclick']}',0,
    '{$add['totaldown']}','{$newspath}','{$filename}','{$userid}',
    '".addslashes($username)."',       ← addslashes
    '{$add['firsttitle']}','{$add['isgood']}','{$add['ispic']}','{$add['istop']}',
    '{$isqf}',0,'{$add['isurl']}','{$truetime}','{$lastdotime}',
    '{$havehtml}','{$add['groupid']}','{$add['userfen']}',
    '".addslashes($add['my_titlefont'])."',  ← addslashes
    '".addslashes($add['titleurl'])."',       ← addslashes
    '{$ret_r['tb']}','{$public_r['filedeftb']}','{$public_r['pldeftb']}',
    '".addslashes($add['keyboard'])."'       ← addslashes
    {$ret_r['values']}             ← 自定义字段(已含addslashes)
);","ins");

数字字段直接用 {$add['classid']}(已 (int) 处理过),字符串字段用 addslashes() 包裹。


五、完整防护矩阵

POST 提交
  │
  ├─ 数字字段 ──→ (int) 强转 ──→ 100% 安全
  │
  ├─ 标准字符串 ──→ eDoRepPostComStr (HTML转义)
  │               └─→ hRepPostStr (SQL拦截 + addslashes)
  │
  ├─ 文件路径  ──→ RepFilenameQz (路径净化)
  │               └─→ eCheckStrType_fname (类型校验)
  │
  └─ 自定义字段 ──→ RepPhpAspJspcodeText (PHP代码清除)
                    └─→ hEModCkFieldType (类型校验)
                    └─→ hRepPostStr2 (SQL拦截 + addslashes,保留HTML)
                    └─→ addslashes (入库前再包裹一层)

六、常见误解澄清

误解1:ecmsinfo.php 直接传 $_POST 不安全

// ecmsinfo.php:53
AddNews($_POST, $logininid, $loginin);

看起来像裸传,但实际上安全过滤全部在 AddNews() 内部完成。这是一种收口设计——所有入口统一经过业务函数,不会漏掉过滤。

误解2:自定义字段(newstext)没有过滤

实际上自定义字段经历了比标准字段更长的过滤链——RepPhpAspJspcodeText → hRepPostStr2(SQL过滤) → addslashes,而且因为 hRepPostStr2 不做 HTML 转义,富文本内容能正常保留。

误解3:双重 addslashes 会导致数据错误

PHP 的 addslashes 对已转义的字符再次加反斜杠(' → \' → \\'),但在帝国CMS的流程中,hRepPostStr2 的 AddAddsData 处理原始值,而最后的 addslashes 是拼 SQL 时加的——它们处理的是同一个值在不同阶段的安全需求,实际不会叠加。


七、总结

帝国CMS信息发布功能的防注入设计可以概括为三条原则

  1. 数字用 (int),字符串用过滤函数——分类处理,不混用
  2. HTML转义和SQL过滤分离——hRepPostStr2 只做SQL防护不做HTML转义,满足富文本场景
  3. 收口到业务函数——入口文件只做路由,过滤集中在 DoPostInfoVar() 和 ReturnAddF() 中

这种分层设计虽然代码看起来分散,但每一层职责清晰,组合起来形成了纵深防御。

微信扫码咨询

微信二维码