SQL注入是Web应用中最常见也是最危险的漏洞之一,织梦DedeCMS作为国内流行的CMS系统,由于其早期版本对用户输入的过滤不够严格,历史上曾多次曝出SQL注入漏洞,为V5.7版本进行安全加固是非常必要的。

防护SQL注入的核心思想是:永远不要信任用户的输入,所有来自客户端的数据(GET、POST、Cookie等)都应该被视为不可靠的,必须进行严格的过滤和验证。
以下是针对织梦V5.7的SQL注入防护方案,分为通用安全加固和针对已知历史漏洞的专项修复两部分。
通用安全加固(普适性安全原则)
这些措施适用于所有PHP网站,是防御SQL注入的基础。
使用PDO预处理语句
这是最有效、最推荐的防御SQL注入的方法,它将SQL语句和数据分开处理,从根本上杜绝了SQL注入的可能性。

织梦V5.7的修改:
织梦的数据库操作封装在 /include/dedesql.class.php 文件中,要全面转向PDO,工作量较大,但我们可以修改核心类,让它在执行查询时默认使用预处理。
修改 /include/dedesql.class.php:
找到 SetQuery() 和 ExecuteQuery() 等方法,将原有的字符串拼接SQL查询的方式,修改为使用预处理语句。

示例(修改思路):
// 原来的方式(危险)
$this->SetQuery("SELECT * FROM dede_archives WHERE id = {$id}");
$this->ExecuteQuery('select');
// 修改后的方式(安全)
public function safeQuery($sql, $params = array()) {
// 确保已经连接数据库
if (!$this->linkID) return false;
// 准备SQL语句
$this->queryString = $sql;
if ($this->result = $this->linkID->prepare($sql)) {
// 绑定参数并执行
if ($this->result->execute($params)) {
return $this->result;
} else {
$this->DisplayError("SQL语句执行错误: " . $this->linkID->errorInfo());
return false;
}
} else {
$this->DisplayError("SQL语句预处理失败: " . $this->linkID->errorInfo());
return false;
}
}
注意: 全面修改织梦的数据库底层是一个大工程,需要非常谨慎,可能会影响现有功能,对于不想大改的用户,可以采用下面第二种方法。
使用 mysql_real_escape_string 或 addslashes 进行转义
如果无法全面转向PDO,那么必须在所有SQL查询执行前,对用户输入进行转义处理。
修改 /include/dedesql.class.php:
在 SetQuery() 方法中,自动对变量进行转义。
// 在 desql.class.php 中找到 SetQuery 方法
function SetQuery($sql) {
// ... 原有代码 ...
// 新增:自动转义查询中的变量
// 这是一个简单的正则,查找类似 `WHERE field = '$var'` 或 `WHERE field = "$var"` 的模式
// 并对变量进行转义,注意:这种方法不如预处理语句健壮。
$sql = preg_replace("/(SELECT|UPDATE|INSERT|DELETE|DROP|CREATE|ALTER|TRUNCATE|SHOW|EXEC|UNION|INTO|LOAD_FILE|OUTFILE)\s+/i", " ", $sql); // 简单过滤关键字
// 对单引号、双引号进行转义
if (get_magic_quotes_gpc()) {
$sql = stripslashes($sql);
}
$this->queryString = mysql_real_escape_string($sql, $this->linkID);
// ... 原有代码 ...
}
更推荐的做法: 在每个接收用户输入的文件中,手动进行转义,在处理搜索请求的 plus/search.php 中:
// 在 plus/search.php 中 require_once(dirname(__FILE__)."/../include/config_base.php"); require_once(DEDEINC.'/dedetemplate.class.php'); require_once(DEDEINC.'/dedesql.class.php'); // 获取搜索关键词 $keyword = isset($keyword) ? trim($keyword) : ''; $keyword = addslashes($keyword); // 手动转义 // ... 后续使用 $keyword 构建SQL ...
开启PHP的魔术引号(已废弃,不推荐,但可作为一种兼容方案)
magic_quotes_gpc 是PHP的一个旧特性,它会自动为 GET, POST, COOKIE 数据中的单引号、双引号、反斜杠和 NULL 字符加上反斜杠。
- 开启状态:可以提供一层基础保护,但不推荐依赖它,因为它可能导致数据二次转义,且已在PHP 5.4.0中移除。
- 关闭状态:必须手动对所有用户输入进行转义,如上所述。
检查方法:
echo (get_magic_quotes_gpc() ? '开启' : '关闭');
修改 php.ini 配置
register_globals = Off:这是非常重要的设置,如果开启,任何提交的变量都会自动成为全局变量,极易被恶意利用,确保它设置为Off。display_errors = Off:在生产环境中,关闭错误显示,详细的错误信息(如数据库结构、路径等)会泄露给攻击者。log_errors = On:开启错误日志,方便管理员排查问题。
针对织梦V5.7已知历史漏洞的专项修复
除了通用加固,我们还需要修复织梦历史上曝出的具体SQL注入点,这些漏洞通常存在于接收用户输入并直接用于查询的文件中。
漏洞点一:plus/search.php 搜索注入
问题: 搜索关键词 keyword 未经过滤,直接拼接到SQL中。
修复方法:
打开 /plus/search.php 文件。
找到类似这样的代码:
// 危险代码示例
$row = $dsql->GetOne("SELECT * FROM `#@__search_keywords` WHERE keyword LIKE '{$keyword}%' ");
修改为:
// 安全代码
$keyword = isset($keyword) ? trim($keyword) : '';
$keyword = addslashes($keyword); // 或使用 mysql_real_escape_string
// 如果使用PDO,最佳实践是:
// $sql = "SELECT * FROM `#@__search_keywords` WHERE keyword LIKE ?";
// $row = $dsql->GetOne($sql, array($keyword . '%'));
// 如果必须用字符串拼接,确保已转义
if ($keyword != '') {
$row = $dsql->GetOne("SELECT * FROM `#@__search_keywords` WHERE keyword LIKE '{$keyword}%' ");
}
漏洞点二:member/mtjs.php 会员中心JS注入
问题: 会员ID mid 未过滤,直接用于查询。
修复方法:
打开 /member/mtjs.php 文件。
找到类似这样的代码:
// 危险代码示例
$mid = isset($mid) ? $mid : 0;
$row = $dsql->GetOne("SELECT * FROM `#@__member` WHERE mid = $mid ");
修改为:
// 安全代码
$mid = isset($mid) ? intval($mid) : 0; // 使用 intval 强制转换为整数
if ($mid > 0) {
$row = $dsql->GetOne("SELECT * FROM `#@__member` WHERE mid = $mid ");
}
对于ID类字段,使用 intval() 是最简单有效的过滤方法。
漏洞点三:plus/list.php 列表页注入
问题: tid (栏目ID) 等参数未过滤。
修复方法:
打开 /plus/list.php 文件。
找到获取 tid 的地方:
// 危险代码示例 $tid = isset($tid) ? $tid : 0;
修改为:
// 安全代码 $tid = isset($tid) ? intval($tid) : 0;
漏洞点四:feedback.php 评论/留言本注入
问题: msg (留言内容)、aid (文章ID) 等参数未过滤。
修复方法:
打开 /feedback.php 文件。
// 危险代码示例
$msg = htmlspecialchars($msg); // htmlspecialchars只转义HTML,不转义SQL
$aid = isset($aid) ? $aid : 0;
// 插入数据库时...
$dsql->ExecuteNoneQuery("INSERT INTO `#@__feedback` (`aid`, `mid`, `msg`, `ip`) VALUES ('$aid', '$mid', '$msg', '$ip')");
修改为:
// 安全代码
$msg = isset($msg) ? trim($msg) : '';
$msg = addslashes($msg); // 必须对SQL进行转义
$aid = isset($aid) ? intval($aid) : 0;
$mid = isset($mid) ? intval($mid) : 0;
$ip = GetIP();
// 插入数据库时...
$dsql->ExecuteNoneQuery("INSERT INTO `#@__feedback` (`aid`, `mid`, `msg`, `ip`) VALUES ('$aid', '$mid', '$msg', '$ip')");
综合安全建议
- 及时更新: 访问织梦官方论坛,关注安全公告,及时下载并安装最新的安全补丁,这是最省心的方法。
- 文件权限: 将网站目录的权限设置为
755,文件权限设置为644,确保data、templets等可写目录的权限尽可能严格(如755或750),防止Web用户写入恶意文件。 - 删除无用文件: 删除安装目录
/install和后台管理目录/dede/下的install_lock.txt(如果存在),以及所有演示数据文件。 - 使用安全插件: 可以考虑使用一些专门为织梦开发的安全插件,它们通常集成了防注入、防篡改、后台登录验证等功能。
- 定期备份: 定期备份网站文件和数据库,以便在网站被入侵后能快速恢复。
为织梦V5.7防SQL注入,最佳实践是:
- 核心思想:对所有用户输入进行“不信任”原则处理。
- 最佳方案:全面改造数据库操作类,使用PDO预处理语句,这是最根本、最彻底的解决方法。
- 次优方案:如果不想大改代码,则在所有接收用户输入的文件中,手动使用
addslashes()或mysql_real_escape_string()对变量进行转义,并对ID类参数使用intval()强制转换。 - 专项修复:针对已知的、高发的历史漏洞点(如
plus/search.php、member/mtjs.php等)进行逐一排查和修复。 - 辅助措施:结合文件权限管理、及时更新、定期备份等综合安全手段,构建一个纵深防御体系。
请务必在修改前备份您的网站文件和数据库,以防修改导致意外错误。
