【问题标题】:Handle $_GET safely PHP安全处理 $_GET PHP
【发布时间】:2012-03-18 14:38:03
【问题描述】:

我有这样的代码:

$myvar=$_GET['var'];

// a bunch of code without any connection to DB where $myvar is used like this:

$local_directory=dirname(__FILE__).'/images/'.$myvar;
if ($myvar && $handle = opendir($local_directory)) {
    $i=0; 
    while (false !== ($entry = readdir($handle))) {
        if(strstr($entry, 'sample_'.$language.'-'.$type)) {
            $result[$i]=$entry;
            $i++;
        }
    } 
    closedir($handle);
} else {
    echo 'error';
}

我对一些剥离和转义函数有点困惑,所以问题是,我需要对$myvar 做什么才能确保这段代码安全?就我而言,我没有建立任何数据库连接。

【问题讨论】:

  • .$myvar 目录是否已经存在,还是您即时创建的?
  • @Pekka:该代码不会即时创建任何内容。
  • 是否使用 php 过滤器清理所有输入,对于您的代码来说应该绰绰有余。 br.php.net/manual/en/filter.filters.sanitize.php
  • @Niklas 根据 OP,上面的代码只是摘录。
  • Smartass 回答:没有,因为您使用的是single quoted string,所以您尝试访问的目录实际上是images/.$myvar,美元符号等等。

标签: php security get


【解决方案1】:

您正在尝试防止目录遍历攻击,因此您不希望有人输入./../../../ 或其他东西,希望读出文件或文件名,具体取决于您在做什么。

我经常使用这样的东西:

$myvar =  preg_replace("/[^a-zA-Z0-9-]/","",$_GET['var']);

这会将不是a-zA-Z0-9- 的任何内容替换为空白,因此如果变量包含*,此代码将删除它。

然后我更改 a-zA-Z0-9- 以匹配我希望在字符串中允许的字符。然后我可以将其锁定为仅包含数字或我需要的任何内容。

【讨论】:

  • 在大多数情况下都能正常工作。当您在同级文件夹中有要保护的数据时,应注意。这就是为什么我倾向于明确说明允许的内容,而不是试图阻止我避免的内容。
  • 是的,当然。在非常清楚的情况下,您可以将正则表达式更改为类似 "/^(option1|option2|option3)"/ 的内容。始终尽可能限制输入。
  • 确实是所有好的建议。我倾向于依赖数组(或其他更复杂情况下的东西),因为它可以灵活地与 FS 保持同步并避免重复的代码/逻辑。但是,嘿,无论如何都是好点。
  • 是的,这真的取决于上下文 - 使用像这样的正则表达式是一个很好的快速简单的解决方案,可以不假思索地投入使用,虽然是的,但通常你会想要与已知值进行比较从数据库。真的取决于你在做什么。加上正则表达式让我感觉自己像个编码大师。 :)
  • 啊啊啊。绝对好点。我知道那是什么感觉。 :-)
【解决方案2】:

这样做真的非常危险:opendir($local_directory) 其中$local_directory 是一个可能来自外部的值。

如果有人传入../../../../../../../../../etc ...或类似的东西怎么办?您可能会危及主机的安全性。

您可以在这里浏览一下,开始: http://php.net/manual/en/book.filter.php

恕我直言,如果您不即时创建任何东西,您应该有类似的东西:

$allowed_dirs = array('dir1','dir2', 'dir3');
if (!in_array($myvar, $allowed_dirs)) {
    // throw an error and log what has happened
}

您可以在收到来自“外部”的输入后立即执行此操作。如果您这样做不切实际,因为图像目录的数量会随时间而变化,并且您害怕错过与代码库的同步,您还可以填充有效值数组,扫描您拥有的子目录到图像中首先是文件夹。

所以,最后,你可以有类似的东西:

$allowed_dirs = array();
if ($handle = opendir(dirname(__FILE__) . '/images')) {
    while (false !== ($entry = readdir($handle))) {
            $allowed_dirs[] = $entry;
    } 
    closedir($handle);
}
$myvar=$_GET['var'];

// you can deny access to dirs you want to protect like this
unset($allowed_dirs['private_stuff']);

// rest of code
$local_directory = dirname(__FILE__) . "/images/.$myvar";
if (in_array(".$myvar", $allowed_dirs) && $handle = opendir($local_directory)) {
    $i=0; 
    while (false !== ($entry = readdir($handle))) {
        if(strstr($entry, 'sample_'.$language.'-'.$type)) {
            $result[$i]=$entry;
            $i++;
        }
    } 
    closedir($handle);
} else {
    echo 'error';
}

上面的代码没有优化。但是在这种情况下,让我们避免过早的优化(这样说是为了避免另一个“不错的”反对票); sn-p 只是为了让您了解明确允许值 VS 允许一切的替代方法,除非匹配特定模式。我认为前者更安全。

【讨论】:

  • 任何示例如何过滤它?
  • 语法高亮的好处是某些错误立即可见。
  • 我寻找我想要动态处理的有效子文件夹。然后我只允许在这些文件夹中指定外部参数。我不允许传入任何其他内容。
  • 因为您似乎允许images 的任何子目录作为有效输入,您也可以完全不使用目录白名单,而只使用更简单的基于正则表达式的解决方案。请记住,如果目录不存在,opendir 已经失败。
  • 你说得对,Niklas B。我不想在上面的代码中过于全面。但是数组解决方案很容易让您取消设置您不希望允许用户访问的目录(在扫描源目录时或以后),但可以容纳动态值。顺便说一句,你今天对我来说似乎很严厉。我一定对你做了什么。我道歉,不是我的本意! ;-)
【解决方案3】:

为了完整性,请注意,如果您可以确定您的代码只能在 Unixish 系统(例如 Linux)上运行,只有您需要的东西以确保:

  1. $myvar 不包含任何斜杠("/",U+002F)或空("\0",U+0000)字符,而且

  2. $myvar 不为空或不等于 "."(或者,等效地,".$myvar" 不等于 ".""..")。

这是因为,在 Unix 文件系统上,唯一的目录分隔符(以及文件名中不允许的两个字符之一,另一个是空字符 "\0")是斜线,并且唯一的特殊目录条目指向上方在目录树中是"."".."

但是,如果有一天您的代码可能会在 Windows 上运行,那么您将需要禁止更多字符(至少是反斜杠、"\\",可能还有其他字符)。我对 Windows 文件系统约定不够熟悉,无法准确说明您需要在那里禁止哪些字符,但 safe 方法是执行 as Rich Bradshaw suggests 并且 only 允许您知道的字符是安全的。

【讨论】:

    【解决方案4】:

    与来自不受信任来源的每个数据一样:在使用前对其进行验证,并在将其传递到另一个上下文时对其进行正确编码。

    对于前者,您首先需要指定数据必须具有哪些属性才能被视为有效。这主要取决于其使用目的。

    在您的情况下,$myvar 的值可能至少应该是一个有效的目录名称,但它也可以是由目录名称组成的有效相对路径,具体取决于您的要求。此时,您应该指定这些要求。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 2012-01-07
      • 1970-01-01
      • 2015-07-18
      相关资源
      最近更新 更多