在PHP中保护路径

时间:2010-12-23 21:49:53

标签: php security

我正在编写一些PHP,它将一些路径带到不同的内容目录,并在以后使用这些路径包含各个页面的部分。我正在努力确保路径看起来像它们一样,并且它们都没有违反应用程序的规则。哪个是,

  1. PRIVATEDIR(相对于DOCUMENT_ROOT定义)必须高于DOCUMENT_ROOT
  2. CONTENTDIR(相对于PRIVATEDIR定义)必须位于PRIVATEDIR下方,且不得返回DOCUMENT_ROOT
  3. 剩余的*DIRS(相对于CONTENTDIR定义)必须位于CONTENTDIR
  4. 之下

    我在单例控制器类中设置了一些默认值,然后用户将要覆盖的路径数组传递给此类构造函数。然后我想要理智地检查它们以确保它们遵守上述规则。继承人我是如何开始的......

    编辑: 请注意我在下面的代码中使用error_reporting,然后不要自己动手!我误解了这个命令是如何工作的。如果你想知道为什么,请参阅stealthyninja和评论Shrapnel在评论中的评论(并感谢他们向我指出这一点)。

    private $opts = array( // defaults
       'PRIVATEDIR'   => '..',        // relative to document root
       'CONTENTDIR'   => 'content',   // relative to private dir
       ...
       ...
    );
    
    private function __construct($options) { //$options is the user defined options
        error_reporting(0);
        if(is_array($options)) {
            $this->opts = array_merge($this->opts, $options);
        }
    
        if($this->opts['STATUS']==='debug') {
            error_reporting(E_ALL | E_NOTICE | E_STRICT);
        }
    
        $this->opts['PUBLICDIR']  = realpath($_SERVER['DOCUMENT_ROOT'])
                                            .DIRECTORY_SEPARATOR;
        $this->opts['PRIVATEDIR'] = realpath($this->opts['PUBLICDIR']
                                            .$this->opts['PRIVATEDIR'])
                                            .DIRECTORY_SEPARATOR;
        $this->opts['CONTENTDIR'] = realpath($this->opts['PRIVATEDIR']
                                            .$this->opts['CONTENTDIR'])
                                            .DIRECTORY_SEPARATOR;
        $this->opts['CACHEDIR']   = realpath($this->opts['CONTENTDIR']
                                            .$this->opts['CACHEDIR'])
                                            .DIRECTORY_SEPARATOR;
        $this->opts['ERRORDIR']   = realpath($this->opts['CONTENTDIR']
                                            .$this->opts['ERRORDIR'])
                                            .DIRECTORY_SEPARATOR;
        $this->opts['TEMPLATEDIR' = realpath($this->opts['CONTENTDIR']
                                            .$this->opts['TEMPLATEDIR'])
                                            .DIRECTORY_SEPARATOR;
    
       ...
       ...
       ...
    
    
        // then here I have to check that PRIVATEDIR is above PUBLICDIR
        // and that all the rest remain within private dir and don't drop 
        // down into (or below) PUBLICDIR again. And die with an error if
        // they don't conform.
    }
    

    事情似乎要做很多工作,尤其是每次访问页面时必须运行,然后才能执行其他操作(例如,检查我正在服务的页面的缓存版本) ),当路径基本上是静态的时候。

    我的一部分人认为网站的维护者(现在是我)应该知道他们提供的路径,并且应该检查他们是否符合规则。但是(我认为)我更明智的一面是说,不,我应该同样负责检查路径是否符合,因为事故确实发生(特别是当网站增长/改变/获得新的维护者......)和如果他们能够被抓住,在安全关键的环境中,就没有理由不抓住他们。

    所以,我几乎决定要检查这些路径,问题是我的方法有多好?这感觉就像是次优的解决方案。你会如何改进它或你会建议什么替代品?有没有解决每次页面加载时必须这样做的问题,并且只是在配置发生变化时才这样做? (那将是理想的,但我认为并不重要。)

    感谢。

3 个答案:

答案 0 :(得分:1)

如何使用strpos查找子字符串?这不是最优雅的解决方案,但如果您只是希望为已经在网站上获得大量权力的人提供一些基本保护。我将如何做到这一点:

// PRIVATEDIR already includes PUBLICDIR at this point
if(strpos($this->opts['PRIVATEDIR'],$this->opts['PUBLICDIR']) !== 0) {
die('ERROR: '.$this->opts['PRIVATEDIR'].' must be located in .'$this->opts['PUBLICDIR']);
}

注意三元组不等于,确保PUBLICDIR出现在PRIVATEDIR字符串的第0位,而不是简单地找不到它或字符串中的其他位置。另外你应该检查它们是不一样的(PUBLICDIR!= PRIVATEDIR),因为你可以将它设置为“./test/../”,这将使你回到“。”。

答案 1 :(得分:1)

我只是将它留给网站管理员来决定目录的位置,并提供上述方便的默认值。有人可能想对服务器上的所有站点使用常见的错误或临时目录,或者将私有目录放在公共目录中并通过.htaccess进行保护(不好的做法,但在一些廉价的虚拟主机上可能是必要的)。

另外,构造函数中array_merging的重点是什么?如果$opts数组在该点之前无法获取值,则在读取代码时会出现混淆,并且在启用E_STRICT时会出错;如果它有默认值或其他内容,那么改变它就像你做的那样会产生误导(如果有人在你的类定义中看到$opts = array('TEMPLATEDIR'=>'/templates'),然后在代码的某处找到对$config->$opts['TEMPLATEDIR']的引用,他将会期望它是/templates);您应该首先在选项中使用完整路径,或者将完整路径保存在不同的变量中。

答案 2 :(得分:0)

如果目标是确保代码保留在正确的目录中以查找文件,为什么使用realpath()而不检查您是否仍然在受约束的目录中?或者你不关心任何包含'../'?

的路径

我推断至少CACHEDIR是可写的 - 但是在文档根目录中 - 从安全角度来看这不是一个好主意。

此外,您正在重写现有变量,而不是从模板值构造新变量 - 即使您在构造函数中执行此操作,对于值,这是一个相当混乱的方法,一旦它们真正应该是最终的宣布。