将上载的图像放在公用文件夹中是否安全?

2022-08-31 01:09:45

我刚刚与我的队友讨论了用户上传的图像在图像库中的位置。我希望对我们建议的方法有更广泛的了解。

我的队友编写了一个控制器 + 操作,该操作调用放置在不可用于公共浏览的文件夹中(即,在服务器上的外部)中的图像文件,并通过标头进行回显。这是安全的,但是由于我们使用Zend框架,它也爬行速度很慢 - 由于执行引导的查询,每次调用图像控制器都会花费我们大约500ms的延迟。这很烦人,因为图片库视图同时显示20多张图像。file_get_contentspublic_html

简而言之,相关代码将是:

class ImageController extends Zend_Controller_Action {
    public function showAction () {
        $filename = addslashes($this->_getParam('filename'));
        if(!is_file($filename)) {
            $filename = APPLICATION_PATH.'/../public/img/nopicture.jpg';
        }
        $this->_helper->viewRenderer->setNoRender(true);
        $this->view->layout()->disableLayout();
        $img = file_get_contents($filename);
        header('Content-Type: image/jpeg');
        $modified = new Zend_Date(filemtime($filename));
        $this->getResponse()
             ->setHeader('Last-Modified',$modified->toString(Zend_Date::RFC_1123))
             ->setHeader('Content-Type', 'image/jpeg')
             ->setHeader('Expires', '', true)
             ->setHeader('Cache-Control', 'public', true)
             ->setHeader('Cache-Control', 'max-age=3800')
             ->setHeader('Pragma', '', true);
        echo $img;
    }
}

然后,在视图中,我们只需调用:

<img src="<?php echo $this->url(array('controller' => 'image', 'action' => 'show', 'filename' => PATH_TO_HIDDEN_LOCATION.'/filename.jpg')); ?>" />

我有一种不同的方法:我更喜欢将原始图像保存在隐藏位置,但是一旦请求,请立即将它们复制到公共位置并提供指向它的链接(使用由cron运行的额外机制,不时擦除公共图像目录,以免浪费空间, 并告诉谷歌不要索引该目录)。该解决方案将文件(每个给定时刻的几个)放在可公开访问的目录中(前提是知道文件名),但也只需要一个视图帮助程序,因此不会启动引导程序:robots.txt

class Zend_View_Helper_ShowImage extends Zend_View_Helper_Abstract {
    public function showImage ($filename) {
        if (!file_exists(PUBLIC_PATH."/img/{$filename}")) {
            if (!copy(PATH_TO_HIDDEN_FILES."/{$filename}",PUBLIC_PATH."/img/{$filename}"))
                $url = PUBLIC_PATH.'/img/nopicture.jpg';
            else
                $url = PUBLIC_PATH."/img/{$filename}";
        } else {
            $url = PUBLIC_PATH."/img/{$filename}"
        }
        return "{$url}";
    }
}

借助此助手,调用在视图中非常简单:

<img src="<?php echo $this->showImage('filename.jpg'); ?>" />

问题:我的方法是否像我的同事所说的那样构成安全威胁?这有哪些潜在的风险?而且,最重要的是,安全威胁(如果有的话)是否超过了页面加载的10秒?

如果这很重要:我们正在开发一个拥有大约15K注册用户的社区门户,其中画廊是一个非常常用的功能。

*我粘贴的代码是我们每个人想出的经过编辑的简化版本 - 只是为了展示这两种方法的机制。


答案 1

我有不同的方法:我更喜欢将原始图像保存在隐藏位置,但是一旦收到请求,请立即将它们复制到公共位置并提供指向它的链接

+1 创造力。

我的方法是否像我的同事所说的那样构成安全威胁?这有哪些潜在的风险?而且,最重要的是,安全威胁(如果有的话)是否超过了页面加载的10秒?

差不多吧。是的,如果您只有某些人可以查看图像,并且您将它们放入可公开访问的目录中,则其他人可以看到该图像,这似乎是不可取的。我也不认为(可能是错误的)它会在页面加载时获得10秒,因为您必须复制图像,这是一个相当密集的操作,而不是使用file_get_contents或readfile( )。

这是安全的,但是由于我们使用Zend框架,它也爬行速度很慢 - 由于执行引导的查询,每次调用图像控制器都会花费我们大约500ms的延迟。

如果我可以建议;针对此特定情况的 nuke Zend 框架。我也在一个相当大的网站上使用Zend框架,所以我知道引导可能需要比你想要的更长的时间。如果你绕过Zend框架,选择普通的PHP,这将显着提高性能。

另外,使用 readfile( ),而不是 file_get_contents( )。有一个很大的区别,file_get_contents在输出之前将整个文件加载到内存中,而readfile可以更有效地执行此操作。


答案 2

只要您通过检查MIME魔术字节和预想器甚至尝试使用GD库加载图像来确保该文件确实是图像文件,您应该没问题。

最初的方法可能通过使用不执行整个引导的专用引导程序来加速。只是您加载图像的必需品。

还可以在公用文件夹中放置一个“普通”.php文件,其中包含一个脚本,该脚本使用 GD 或类似文件夹从非公用文件夹打开和输出图像。只需用类似的东西来称呼它

<a href="image.php?image=foobar.jpg&width=320&height=240" />

在这里,您还必须确保不要盲目信任图像名称,但实际上会检查这是图像目录中的图像,而不是令人讨厌的东西。

我还建议将图像存储在 Web 服务器具有写入权限的非公用文件夹中,然后在公用文件夹中创建一个没有写入权限的符号链接,并将目录列表添加到此图像文件夹,并从那里加载图像。您的Web服务器可以直接为它们提供服务,而不必复制它们。

只是一些建议。


推荐