转到代码视图中的第二行,即连接包含文件之后,将 GetSQLValueString() 函数添加到代码开头,如下所示:
<?php if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") { $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue); switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } } ?>
问题
有些数据库允许您在一个查询中发送多个 SQL 语句。 因此,当您在查询字符串中向动态生成的数据库查询传递参数时,存在潜在的安全风险。黑客可能会尝试通过在现有参数中附加恶意 SQL 语句来修改动态查询中的 URL 或表单变量。这通常称为 SQL 注入攻击。 应当修改 Dreamweaver 创建的某些服务器行为代码,以降低 SQL 注入攻击的风险。 有关 SQL 注入的更多背景信息,请参阅这篇维基百科文章。 (参考: 201713)
注意:此技术说明中描述的问题已在 Dreamweaver 8.0.2 更新程序中得到修复。
此技术说明涵盖 Dreamweaver 的 PHP 服务器行为。对于 ColdFusion、ASP VBScript、ASP JavaScript 和 JSP 服务器行为,有单独的技术说明。ASP.NET 服务器行为不受影响。
解决方案
如果您让查询字符串传递参数,请确保仅传递预期的信息。 Adobe 已创建了 Dreamweaver 8.0.2 更新程序,可降低 SQL 注入攻击的风险。这些修复将整合到 Dreamweaver 的所有未来版本中。更新 Dreamweaver 8 后,您需要将服务器行为重新应用到使用它们的页面,并将这些页面重新部署到您的服务器。
此技术说明的其余部分记录了如何手动编辑 Dreamweaver MX 2004 代码,以防止 PHP 服务器模型受到 SQL 注入攻击。容易受到这些攻击的服务器行为如下所列,以及相应的修复方法:
使用过滤器的记录集
未过滤的记录集无需修改,而过滤的记录集则需要修改。 在下面的示例中,名为 "rs_byDate" 的 Dreamweaver MX 2004 记录集通过从 URL 参数传入的日期值进行筛选。下面突出显示的代码是需要更改的:
<?php $colname_rs_byDate = "1"; if (isset($_GET['relDate'])) { $colname_rs_byDate = (get_magic_quotes_gpc()) ? $_GET['relDate'] : addslashes($_GET['relDate']); } mysql_select_db($database_dreamqa2, $dreamqa2); $query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = '%s'", $colname_rs_byDate); $rs_byDate = mysql_query($query_rs_byDate, $dreamqa2) or die(mysql_error()); $row_rs_byDate = mysql_fetch_assoc($rs_byDate); $totalRows_rs_byDate = mysql_num_rows($rs_byDate); ?>
为了保护记录集免受 SQL 注入攻击,应在页面顶部附近创建一个自定义 GetSQLValueString() 函数。此函数验证查询参数的数据类型。创建函数后,更新记录集代码以使用 GetSQLValueString() 函数。
修改后的代码:
$colname_rs_byDate = $_GET['relDate'];
更新 recordset 代码,使用上面创建的 GetSQLValueString() 函数创建 SQL 字符串。更新变量 $query_rs_byDate 的值:
原始代码:
$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = '%s'", $colname_rs_byDate);
修改后的安全代码:
$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = %s", GetSQLValueString($colname_rs_byDate, "date"));
插入、更新和删除记录服务器行为以及记录插入表单向导
插入、更新和删除记录服务器行为以及记录插入表单向导已经使用了 GetSQLValueString() 函数,因此需要对其进行改进以防止 SQL 注入。唯一需要更改的是 GetSQLValueString() 函数。
以下是原始代码。需要更改的行以黄色突出显示:
if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") {$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue; switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } }
更新变量 $theValue 的定义:
原始代码:
\n
\n$theValue = (!get_magic_quotes_gpc()) ?addslashes($theValue) : $theValue;
修改后的安全代码:
\n
\n$theValue = get_magic_quotes_gpc() ?stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);
“登录用户”服务器行为
以下是修复"用户登录"服务器行为的步骤:
修改构造登录 SQL 查询的代码。
原始代码:
$LoginRS__query=sprintf("SELECT username, password FROM login WHERE username='%s' AND password='%s'", get_magic_quotes_gpc() ?$loginUsername : addslashes($loginUsername), get_magic_quotes_gpc() ?$password : addslashes($password));
修改后的安全代码:
\n
\n$LoginRS__query=sprintf(\"SELECT username, password FROM login WHERE username=%s AND password=%s\", GetSQLValueString($loginUsername, \"text\"), GetSQLValueString($password, \"text\"));
“检查新用户”服务器行为
“检查新用户”服务器行为需要先将“插入记录”服务器行为添加到页面。 GetSQLValuesString() 函数包含在"插入记录"服务器行为中。只需要更新 GetSQLValuesString() 以更好地处理 SQL 注入,并更新"检查新用户"服务器行为以在传递参数时使用该函数。
修改代码以构造登录 SQL 查询,使用 GetSQLValuesString() 传递参数。在页面顶部附近,找到以// *** Redirect if username exists.
原始代码:
$LoginRS__query = "SELECT username FROM login WHERE username='" .$loginUsername ."'"
修改后的安全代码:
\n
\n$LoginRS__query = sprintf(\"SELECT username FROM login WHERE username=%s\", GetSQLValueString($loginUsername, \"text\"));
主详细页集
对于“主详细页集”应用程序对象,更改主要在 Dreamweaver 为详细页创建的记录集中进行。 如果主页上没有过滤的记录集,则无需对主页进行任何更改。 如果您有一个筛选记录集,请参阅带有筛选条件的记录集以更新记录集代码。
Dreamweaver 在详细信息页面中创建筛选记录集,因此该记录集代码需要更新为使用 GetSQLValueString() 函数来传递参数并使用 sprintf() 构造 SQL 字符串。
更新变量声明代码并使用 GetSQLValuesString() 函数构造 SQL 查询。
\n
\n
\n
\n原始代码:
\n
\n$recordID = $_GET['recordID']; $query_DetailRS1 = \"SELECT * FROM albums WHERE ID = $recordID\";
修改的安全代码:
$colname_DetailRS1 = "-1"; if (isset($_GET['recordID'])) { $colname_DetailRS1 = (get_magic_quotes_gpc()) ?$_GET['recordID'] : addslashes($_GET['recordID']); } $query_DetailRS1 = sprintf(\"SELECT * FROM albums WHERE ID = %s\", GetSQLValueString($colname_DetailRS1, \"int\"));
记录更新表单向导
其他信息
有关 SQL 注入的更多信息,请参阅以下文章:Wikipedia 关于 SQL 注入的文章。
Adobe 安全公告:APSB 06-07 Dreamweaver 服务器行为 SQL 注入漏洞。