快捷搜索:   nginx

确保PHP应用程序的安全[4]

浏览器内的数据操纵

有一类浏览器插件允许用户篡改页面上的头部元素和表单元素。使用 Tamper Data(一个 Mozilla 插件),可以很容易地操纵包含许多隐藏文本字段的简单表单,从而向 PHP 和 MySQL 发送指令。

用户在点击表单上的 Submit 之前,他可以启动 Tamper Data。在提交表单时,他会看到表单数据字段的列表。Tamper Data 允许用户篡改这些数据,然后浏览器完成表单提交。

让我们回到前面建立的示例。已经检查了字符串长度、清除了 HTML 标记并删除了十六进制字符。但是,添加了一些隐藏的文本字段,如下所示:


清单 17. 隐藏变量

<?php
if ($_POST['submit'] == "go"){
//strip_tags
$name = strip_tags($_POST['name']);
$name = substr($name,0,40);
//clean out any potential hexadecimal characters
$name = cleanHex($name);
//continue processing....
}

function cleanHex($input){
$clean = \
preg_replace("![\][xX]([A-Fa-f0-9]{1,3})!", "",$input);
return $clean;
}
?>

<form action=\
"<?php echo $_SERVER['PHP_SELF'];?>" method="post">
<p><label for=\"name">Name</label>
<input type=\
"text" name="name" id="name" size="20" maxlength="40"/></p>
<input type="hidden" name="table" value="users"/>
<input type="hidden" name="action" value="create"/>
<input type="hidden" name="status" value="live"/>
<p><input type="submit" name="submit" value="go"/></p>
</form>

注意,隐藏变量之一暴露了表名:users。还会看到一个值为 create 的 action 字段。只要有基本的 SQL 经验,就能够看出这些命令可能控制着中间件中的一个 SQL 引擎。想搞大破坏的人只需改变表名或提供另一个选项,比如 delete。

图 1 说明了 Tamper Data 能够提供的破坏范围。注意,Tamper Data 不但允许用户访问表单数据元素,还允许访问 HTTP 头和 cookie。


图 1. Tamper Data 窗口

 

要防御这种工具,最简单的方法是假设任何用户都可能使用 Tamper Data(或类似的工具)。只提供系统处理表单所需的最少量的信息,并把表单提交给一些专用的逻辑。例如,注册表单应该只提交给注册逻辑。

如果已经建立了一个通用表单处理函数,有许多页面都使用这个通用逻辑,那该怎么办?如果使用隐藏变量来控制流向,那该怎么办?例如,可能在隐藏表单变量中指定写哪个数据库表或使用哪个文件存储库。有 4 种选择:

不改变任何东西,暗自祈祷系统上没有任何恶意用户。 重写功能,使用更安全的专用表单处理函数,避免使用隐藏表单变量。 使用 md5() 或其他加密机制对隐藏表单变量中的表名或其他敏感信息进行加密。在 PHP 端不要忘记对它们进行解密。 通过使用缩写或昵称让值的含义模糊,在 PHP 表单处理函数中再对这些值进行转换。例如,如果要引用 users 表,可以用 u 或任意字符串(比如 u8y90x0jkL)来引用它。

后两个选项并不完美,但是与让用户轻松地猜出中间件逻辑或数据模型相比,它们要好得多了。

现在还剩下什么问题呢?远程表单提交。

远程表单提交

Web 的好处是可以分享信息和服务。坏处也是可以分享信息和服务,因为有些人做事毫无顾忌。

以表单为例。任何人都能够访问一个 Web 站点,并使用浏览器上的 File > Save As 建立表单的本地副本。然后,他可以修改 action 参数来指向一个完全限定的 URL(不指向 formHandler.php,而是指向 http://www.yoursite.com/formHandler.php,因为表单在这个站点上),做他希望的任何修改,点击 Submit,服务器会把这个表单数据作为合法通信流接收。

首先可能考虑检查 $_SERVER['HTTP_REFERER'],从而判断请求是否来自自己的服务器,这种方法可以挡住大多数恶意用户,但是挡不住最高明的黑客。这些人足够聪明,能够篡改头部中的引用者信息,使表单的远程副本看起来像是从您的服务器提交的。

处理远程表单提交更好的方式是,根据一个惟一的字符串或时间戳生成一个令牌,并将这个令牌放在会话变量和表单中。提交表单之后,检查两个令牌是否匹配。如果不匹配,就知道有人试图从表单的远程副本发送数据。

要创建随机的令牌,可以使用 PHP 内置的 md5()、uniqid() 和 rand() 函数,如下所示:


清单 18. 防御远程表单提交

<?php
session_start();

if ($_POST['submit'] == "go"){
//check token
if ($_POST['token'] == $_SESSION['token']){
//strip_tags
$name = strip_tags($_POST['name']);
$name = substr($name,0,40);
//clean out any potential hexadecimal characters
$name = cleanHex($name);
//continue processing....
}else{
//stop all processing! remote form posting attempt!
}
}

$token = md5(uniqid(rand(), true));
$_SESSION['token']= $token;


function cleanHex($input){
$clean = preg_replace("![\][xX]([A-Fa-f0-9]{1,3})!", "",$input);
return $clean;
}
?>


<form action="<?php echo $_SERVER['PHP_SELF'];?>" method="post">
<p><label for="name">Name</label>
<input type="text" name="name" id="name" size="20" maxlength="40"/></p>
<input type="hidden" name="token" value="<?php echo $token;?>"/>
<p><input type="submit" name="submit" value="go"/></p>
</form>

这种技术是有效的,这是因为在 PHP 中会话数据无法在服务器之间迁移。即使有人获得了您的 PHP 源代码,将它转移到自己的服务器上,并向您的服务器提交信息,您的服务器接收的也只是空的或畸形的会话令牌和原来提供的表单令牌。它们不匹配,远程表单提交就失败了。

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论