首页 > 代码交流 > > 正文
进入 域名交易资讯论坛

PHP 4.2书写安全的脚本!(转)[discuz1.xx有安全问题就是和这个一样的]

作者:梦    来源:落伍者论坛   更新时间:07-01点击:评论:0

发表于 2003-7-1 15:55LjL中国域名交易资讯网

PHP 4.2书写安全的脚本!(转)[discuz1.xx有安全问题就是和这个一样的]

[b][color=Red][size=6]重要提示:反正已经过去了。以前的discuz1.xx有安全问题就是和这个一样的。他的$admin没有验证。[/size][/color][/b]LjL中国域名交易资讯网
在很长一段时间内,PHP作为服务器端脚本语言的最大卖点之一就是会为从表单提交的值自动建立一个全局变量。在PHP 4.1中,PHP的制作者们推荐了一个访问提交数据的替代手段。在PHP 4.2中,他们取消了那种老的做法!正如我将在这篇文章中解释的那样,作出这样的变化的目的是出于安全性的考虑。我们将研究PHP在处理表单提交及其它数据时的新的做法,并说明为什么这样做会提高代码的安全性。LjL中国域名交易资讯网
LjL中国域名交易资讯网
这里有什么错误?LjL中国域名交易资讯网
LjL中国域名交易资讯网
    看看下面的这段PHP脚本,它用来在输入的用户名及口令正确时授权访问一个Web页面:LjL中国域名交易资讯网
<?phpLjL中国域名交易资讯网
  // 检查用户名及口令LjL中国域名交易资讯网
  if ($username == 'kevin' and $password == 'secret')LjL中国域名交易资讯网
    $authorized = true;LjL中国域名交易资讯网
?>LjL中国域名交易资讯网
<?php if (!$authorized): ?>LjL中国域名交易资讯网
  <!-- 未授权的用户将在这里给予提示 -->LjL中国域名交易资讯网
  <p>Please enter your username and password:</p>LjL中国域名交易资讯网
  <form action="<?=$PHP_SELF?>" method="POST">LjL中国域名交易资讯网
    <p>Username: <input type="text" name="username" /><br />LjL中国域名交易资讯网
       Password: <input type="password" name="password" /><br />LjL中国域名交易资讯网
       <input type="submit" /></p>LjL中国域名交易资讯网
  </form>LjL中国域名交易资讯网
<?php else: ?>LjL中国域名交易资讯网
  <!-- 有安全要求的HTML内容 -->LjL中国域名交易资讯网
<?php endif; ?>LjL中国域名交易资讯网
    OK,我相信大约半数的读者会不屑的说“太愚蠢了-- 我不会犯这样的错误的!”但是我保证有很多的读者会想“嗨,没什么问题啊,我也会这么写的!”当然还会有少数人会对这个问题感到困惑(“什么是PHP?”)。PHP被设计为一个“好的而且容易的”脚本语言,初学者可以在很短的时间内学会使用它;它也应该能够避免初学者犯上面的错误。LjL中国域名交易资讯网
    再回到刚才的问题,上面的代码中存在的问题是你可以很容易地获得访问的权力,而不需要提供正确的用户名和口令。只在要你的浏览器的地址栏的最后添加?authorized=1。因为PHP会自动地为每一个提交的值创建一个变量 -- 不论是来自动一个提交的表单、URL查询字符串还是一个cookie -- 这会将$authorized设置为1,这样一个未授权的用户也可以突破安全限制。LjL中国域名交易资讯网
    那么,怎么简单地解决这个问题呢?只要在程序的开头将$authorized默认设置为false。这个问题就不存在了!$authorized是一个完全在程序代码中创建的变量;但是为什么开发者得为每一个恶意的用户提交的变量担心呢?LjL中国域名交易资讯网
LjL中国域名交易资讯网
PHP 4.2作了什么改变?LjL中国域名交易资讯网
LjL中国域名交易资讯网
    在PHP 4.2中,新安装的PHP中的register_globals选项默认为关闭,因此EGPCS值(EGPCS是Environment、Get、Post、Cookies、Server的缩写 -- 这是PHP中外部变量来源的全部范围)不会被作为全局变量来创建。当然,这个选项还可以通过手工来开启,但是PHP的开发者推荐你将其关闭。要贯彻他们的意图,你需要使用其它的方法来获取这些值。LjL中国域名交易资讯网
    从PHP 4.1开始,EGPCS值就可以从一组指定的数组中获得:LjL中国域名交易资讯网
    $_ENV -- 包含系统环境变量 LjL中国域名交易资讯网
    $_GET -- 包含查询字符串中的变量,以及提交方法为GET的表单中的变量LjL中国域名交易资讯网
    $_POST -- 包含提交方式为POST的表单中的变量LjL中国域名交易资讯网
    $_COOKIE -- 包含所有cookie变量LjL中国域名交易资讯网
    $_SERVER -- 包含服务器变量,例如HTTP_USER_AGENTLjL中国域名交易资讯网
    $_REQUEST -- 包含$_GET、$_POST和$_COOKIE的全部内容LjL中国域名交易资讯网
    $_SESSION -- 包含所有已注册的session变量LjL中国域名交易资讯网
    在PHP 4.1之前,当开发者关闭register_globals选项(这也被考虑为提高PHP性能的一种方法)后,必须使用诸如$HTTP_GET_VARS这样的令人讨厌的名字来获取这些变量。这些新的变量名不仅仅短,而且它们还有其他优点。LjL中国域名交易资讯网
   首先,让我们在PHP 4.2中(也就是说关闭register_globals 选项)重写上面提到的代码:LjL中国域名交易资讯网
<?phpLjL中国域名交易资讯网
  $username = $_REQUEST['username'];LjL中国域名交易资讯网
  $password = $_REQUEST['password'];LjL中国域名交易资讯网
LjL中国域名交易资讯网
// 检查用户名和口令LjL中国域名交易资讯网
if ($username == 'kevin' and $password == 'secret')LjL中国域名交易资讯网
$authorized = true;LjL中国域名交易资讯网
?>LjL中国域名交易资讯网
<?php if (!$authorized): ?>LjL中国域名交易资讯网
<!-- 未授权的用户将在这里给予提示 -->LjL中国域名交易资讯网
<p>Please enter your username and password:</p>LjL中国域名交易资讯网
<form action="<?=$PHP_SELF?>" method="POST">LjL中国域名交易资讯网
<p>Username: <input type="text" name="username" /><br />LjL中国域名交易资讯网
Password: <input type="password" name="password" /><br />LjL中国域名交易资讯网
<input type="submit" /></p>LjL中国域名交易资讯网
</form>LjL中国域名交易资讯网
<?php else: ?>LjL中国域名交易资讯网
<!-- 有安全要求的HTML内容 -->LjL中国域名交易资讯网
<?php endif; ?>LjL中国域名交易资讯网
    正如你看到的,我所需要做的只是在代码的开始增加下面两行:LjL中国域名交易资讯网
  $username = $_REQUEST['username'];LjL中国域名交易资讯网
  $password = $_REQUEST['password'];LjL中国域名交易资讯网
    因为我们希望用户名和密码是由用户提交的,所以我们从$_REQUEST数组中获取这些值。使用这个数组使得用户可以自由选择传递方式:通过URL查询字符串(例如允许用户创建书签时自动输入他们的证书)、通过一个提交的表单或者是通过一个cookie。如果你想要限制只能通过表单提交证书(更精确地说,是通过HTTP POST请求),你可以使用$_POST数组:LjL中国域名交易资讯网
  $username = $_POST['username'];LjL中国域名交易资讯网
  $password = $_POST['password'];LjL中国域名交易资讯网
    除了“引入”这两个变量以外,程序代码没有任何改变。简单地关闭register_globals选项促使开发者更进一步了解哪些数据是来自外部的(不可信任的)资源。LjL中国域名交易资讯网
    请注意这里还有一个小问题:PHP中默认的error_reporting设置仍然是E_ALL & ~E_NOTICE,因此如果“username”和“password”这两个值没有被提交,试图从$_REQUEST数组或$_POST数组中获得这两个值并不会招致任何错误信息。如晨不你的PHP程序需要严格的错误检查,你还需要增加一些代码以首先检查这些变量。LjL中国域名交易资讯网
LjL中国域名交易资讯网
但是这是不是意味着更多的输入?LjL中国域名交易资讯网
LjL中国域名交易资讯网
    是的,在象上面这样的简单程序中,使用PHP 4.2常常会增加输入量。但是,还是看看光明的一面吧 -- 你的程序终究是更安全了!LjL中国域名交易资讯网
    不过认真的说,PHP的设计者并没有完全忽视你的痛苦。在这些新数组中有一个特殊的其它所PHP变量都不具备的特征,它们是完全的全局变量。这对你有什么帮助呢?让我们先对我们的示例进行一下扩充。LjL中国域名交易资讯网
    为了使得站点中的多个页面可以使用用户名/口令论证,我们将我们用户认证程序写到一个include文件(protectme.php)中:LjL中国域名交易资讯网
<?php /* protectme.php */LjL中国域名交易资讯网
function authorize_user($authuser, $authpass)LjL中国域名交易资讯网
{LjL中国域名交易资讯网
$username = $_POST['username'];LjL中国域名交易资讯网
$password = $_POST['password'];LjL中国域名交易资讯网
// 检查用户名和口令LjL中国域名交易资讯网
if ($username != $authuser or $password != $authpass):LjL中国域名交易资讯网
?>LjL中国域名交易资讯网
<!-- 未授权的用户将在这里给予提示 -->LjL中国域名交易资讯网
<p>Please enter your username and password:</p>LjL中国域名交易资讯网
<form action="<?=$PHP_SELF?>" method="POST">LjL中国域名交易资讯网
<p>Username: <input type="text" name="username" /><br />LjL中国域名交易资讯网
Password: <input type="password" name="password" /><br />LjL中国域名交易资讯网
<input type="submit" /></p>LjL中国域名交易资讯网
</form>LjL中国域名交易资讯网
<?phpLjL中国域名交易资讯网
exit();LjL中国域名交易资讯网
endif;LjL中国域名交易资讯网
}LjL中国域名交易资讯网
?>LjL中国域名交易资讯网
现在,我们刚才的页面看上去将是这样的:LjL中国域名交易资讯网
<?phpLjL中国域名交易资讯网
  require('protectme.php');LjL中国域名交易资讯网
  authorize_user('kevin','secret');LjL中国域名交易资讯网
?>LjL中国域名交易资讯网
<!-- 有安全要求的HTML内容 -->LjL中国域名交易资讯网
    很简单,很清晰明了,对不对?现在是考验你的眼力和经验的时候了 -- 在authorize_user 函数中少了什么?LjL中国域名交易资讯网
    在函数中没有申明$_POST是一个全局变量!在php 4.0中,当register_globals开启时,你需要增加一行代码以在函数中获取$username和$password变量:LjL中国域名交易资讯网
  function authorize_user($authuser, $authpass)LjL中国域名交易资讯网
  {LjL中国域名交易资讯网
    global $username, $password;LjL中国域名交易资讯网
...LjL中国域名交易资讯网
    在PHP中,和其它具有类似语法的语言不同,函数外的变量在函数中不能自动获得,你需要象上面所说明的那样增加一行以指定其来自global范围。LjL中国域名交易资讯网
    在PHP 4.0中,当关闭register_globals以提供安全性时,你可以使用$HTTP_POST_VARS数组以获得你的表单提交的值,但是你还是需要从全局范围导入这个数组:LjL中国域名交易资讯网
  function authorize_user($authuser, $authpass)LjL中国域名交易资讯网
  {LjL中国域名交易资讯网
    global $HTTP_POST_VARS;LjL中国域名交易资讯网
$username = $HTTP_POST_VARS['username'];LjL中国域名交易资讯网
$password = $HTTP_POST_VARS['password'];LjL中国域名交易资讯网
    但是在PHP 4.1及以后的版本中,特殊的$_POST变量(以及上面提到的其它变量)可以在所有范围内使用。这就是不需要在函数中申明$_POST变量是一个全局变量的原因:LjL中国域名交易资讯网
  function authorize_user($authuser, $authpass)LjL中国域名交易资讯网
  {LjL中国域名交易资讯网
    $username = $_POST['username'];LjL中国域名交易资讯网
    $password = $_POST['password'];LjL中国域名交易资讯网
LjL中国域名交易资讯网
这对session有什么影响?LjL中国域名交易资讯网
LjL中国域名交易资讯网
    特殊的$_SESSION数组的引入实际上有助于简化session代码。你不需要将session变量申明为全局变量,然后再去留意哪些变量被注册了,你现在可以简单地从$_SESSION['varname']中引用你所有的session变量。LjL中国域名交易资讯网
    现在让我们来看看另一个用户认证的例子。这一次,我们使用sessions以标志一个在你的网站继续逗留的用户已经经过了用户认证。首先,我们来看看PHP 4.0版本(开启register_globals):LjL中国域名交易资讯网
<?phpLjL中国域名交易资讯网
  session_start();LjL中国域名交易资讯网
if ($username == 'kevin' and $password == 'secret')LjL中国域名交易资讯网
{LjL中国域名交易资讯网
$authorized = true;LjL中国域名交易资讯网
session_register('authorized');LjL中国域名交易资讯网
}LjL中国域名交易资讯网
?>LjL中国域名交易资讯网
<?php if (!$authorized): ?>LjL中国域名交易资讯网
<!-- 显示HTML表单以提示用户登录 -->LjL中国域名交易资讯网
<?php else: ?>LjL中国域名交易资讯网
<!-- 有安全要求的HTML内容 -->LjL中国域名交易资讯网
<?php endif; ?>LjL中国域名交易资讯网
    和刚开始的程序一样,这个程序也存在安全漏洞,在URL的最后加上?authorized=1可以绕过安全措施直接访问页面内容。开发者可以将$authorized视为一个session变量而忽视了可以很容易地通过用户输入设置同样的变量。LjL中国域名交易资讯网
    当我们增加了我们的特殊的数组(PHP 4.1)并关闭register_globals(PHP 4.2)后,我们的程序将是这样的:LjL中国域名交易资讯网
<?phpLjL中国域名交易资讯网
  session_start();LjL中国域名交易资讯网
if ($username == 'kevin' and $password == 'secret')LjL中国域名交易资讯网
$_SESSION['authorized'] = true;LjL中国域名交易资讯网
?>LjL中国域名交易资讯网
<?php if (!$_SESSION['authorized']): ?>LjL中国域名交易资讯网
<!-- 显示HTML表单以提示用户登录 -->LjL中国域名交易资讯网
<?php else: ?>LjL中国域名交易资讯网
<!-- 有安全要求的HTML内容 -->LjL中国域名交易资讯网
<?php endif; ?>LjL中国域名交易资讯网
    是不是更加简单了?你不再需要再将普通的变量注册为一个session变量,你只需要直接设置session变量(在$_SESSION数组中),然后用同样的方法使用它。程序变得更短了,而且对于什么变量是session变量也不会引起混乱!LjL中国域名交易资讯网
LjL中国域名交易资讯网
总结LjL中国域名交易资讯网
LjL中国域名交易资讯网
    在这篇文章中,我解释了PHP脚本语言作出改变的深层原因。在PHP 4.1中,添加了一组特殊数据以访问外部数据。这些数组可以在任何范围内调用,这使得外部数据的访问更方便。在PHP 4.2中,register_globals被默认关闭以鼓励使用这些数组以避免无经验的开发者编写出不安全的PHP代码。

街舞 发表于 2003-7-1 16:11LjL中国域名交易资讯网

补充LjL中国域名交易资讯网
LjL中国域名交易资讯网
PHP 4.3.2 增加了屏蔽类的命令LjL中国域名交易资讯网
LjL中国域名交易资讯网
目前在 RC 版本中测试并没有生效,可能正式版本中就会起作用了~LjL中国域名交易资讯网
LjL中国域名交易资讯网
LjL中国域名交易资讯网
代码: [复制到剪贴板] LjL中国域名交易资讯网
; This directive allows you to disable certain classes for security reasons. LjL中国域名交易资讯网
; It receives a comma-delimited list of class names. This directive is LjL中国域名交易资讯网
; *NOT* affected by whether Safe Mode is turned On or Off. LjL中国域名交易资讯网
disable_classes = LjL中国域名交易资讯网
LjL中国域名交易资讯网
LjL中国域名交易资讯网
LjL中国域名交易资讯网
若结合 disable_functions 来使用的话,可以完全关闭一些功能,比如目录操作LjL中国域名交易资讯网
等,disable 掉 opendir, readdir, chdir, getcwd 等函数,再 disable 掉 dir 类。LjL中国域名交易资讯网
目录操作就完全没办法了。

雪人 发表于 2003-7-1 17:09LjL中国域名交易资讯网

一直是用数组获取的LjL中国域名交易资讯网
LjL中国域名交易资讯网
 
评论】【加入收藏夹】【打印】【关闭
  • 上一篇文章:刚才那个音乐小偷的代码!

  • 下一篇文章:站长助手 v1.0 Beta提供下载!

  • 频道最新
    热门排行