Безопасно ли размещать загруженные изображения в общую папку? - PullRequest
16 голосов
/ 08 июля 2011

Я только что обсудил с моим товарищем по команде расположение загруженных пользователем изображений в галерее изображений. Я хотел бы получить более полное представление о методах, которые мы предлагаем.

Мой напарник по команде написал действие + контроллера, которое вызывает file_get_contents для файла изображения, помещенного в папку, недоступную для публичного просмотра (т. Е. Вне public_html на сервере), и отображает его в заголовке. Это безопасно, но, поскольку мы используем Zend Framework, оно также сканируется медленно - каждый вызов контроллера изображения обходится нам примерно в 500 мс лага из-за выполнения запросов начальной загрузки. Это раздражает, поскольку представление галереи изображений отображает более 20 изображений одновременно.

Короче говоря, соответствующий код будет:

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, указывающий Google не индексировать каталог). Решение помещает файлы (по несколько в каждый данный момент) в общедоступный каталог (при условии, что один знает имя файла), но также требует только помощника вида, таким образом, не запускает загрузчик:

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-секундный выигрыш при загрузке страницы?

На случай, если это имеет значение: мы работаем над порталом сообщества, в котором зарегистрировано около 15 000 пользователей, а галереи - очень часто используемая функция.

* Код, который я вставил, является отредактированной, упрощенной версией того, что придумал каждый из нас - просто чтобы показать механику обоих подходов.

Ответы [ 6 ]

6 голосов
/ 08 июля 2011

У меня другой подход: я предпочитаю хранить оригинальные изображения в скрытом месте, но как только они будут запрошены, скопируйте их в общедоступное место и предоставьте ссылку на него

+ 1 для творчества.

Представляет ли мой подход угрозу безопасности, как заявляют мои коллеги?Каковы потенциальные риски этого?И, самое главное, перевешивают ли угрозы безопасности, если таковые имеются, 10-секундный выигрыш при загрузке страницы?

В некотором роде.Да, если у вас есть изображения, которые разрешено видеть только некоторым людям, и вы помещаете их в общедоступный каталог, есть изменение, которое другие люди могут видеть это изображение, что представляется нежелательным.Я также не думаю (возможно, ошибаюсь), что он получит 10 секунд при загрузке страницы, поскольку вам придется копировать изображения, что является довольно интенсивной операцией, а не использованием file_get_contents или readfile ().

Это безопасно, но, поскольку мы используем Zend Framework, оно также ползет медленно - каждый вызов контроллера изображения обходится нам примерно в 500 мс лага из-за выполнения запросов начальной загрузки.

Если я могу предложить;Nuke Zend Framework для этого конкретного случая.Я использую Zend Framework и для довольно большого веб-сайта, поэтому я знаю, что загрузка может занять больше времени, чем вы хотите.Если вы обойдете Zend Framework, выбрав ванильный PHP, это значительно повысит производительность.

Также используйте readfile (), а не file_get_contents ().Существует большая разница в том, что file_get_contents будет загружать весь файл в память перед выводом, где readfile делает это более эффективно.

4 голосов
/ 08 июля 2011

До тех пор, пока вы убедитесь, что файл действительно является файлом изображения, отметив MIME , магические байты и даже попытайтесь загрузить изображение с помощью библиотеки GD, вам следует хорошо.

Первоначальный подход, вероятно, можно было бы ускорить с помощью специализированной начальной загрузки, которая не выполняет всю вашу начальную загрузку. Только самое необходимое для загрузки вашего изображения.

Вы также можете разместить «обычный» .php файл в вашей общей папке, содержащий скрипт, который открывает и выводит изображения из вашей непубличной папки, используя GD или аналогичный. Просто назовите это чем-то вроде

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

Здесь вы также должны убедиться, что вы не доверяете имени изображения вслепую, но на самом деле проверяете, что это изображение в вашем каталоге изображений, а не что-то неприятное.

Я также мог бы порекомендовать хранить ваши изображения в непубличной папке, к которой веб-сервер имеет доступ для записи , а затем создать символическую ссылку без доступа для записи и списка каталогов в папке public в эту папку изображений и загрузите изображения оттуда. Ваш веб-сервер может обслуживать их напрямую и не должен копировать их.

Всего несколько предложений.

2 голосов
/ 08 июля 2011

Я думал о такой проблеме несколько недель назад и закончил тем, что написал собственный Thumbnail ViewHelper, как и вы. Исходные изображения хранятся в непубличном каталоге. Поэтому каждый раз, когда я звоню, как

echo $this->thumb(array('url' => HIDDEN_DIR . 'foo/bar.jpg'));

ViewHelper скопирует изображение в общедоступную директорию кеша и вернет URL-адрес в выгружаемое изображение:

/_cache/thumbs/3858f62230ac3c915f300c664312c63f.jpg

Дополнительно, скрипт может обрезать и изменять размеры изображений, если это необходимо. Кроме того, я добавил функцию для очистки 1000 изображений из кэша каждые 50 вызовов ...

Единственное, что нужно помнить, это то, что вы никогда не показываете путь к каталогу с оригинальными изображениями, даже если он недоступен для общего доступа. Тогда я бы сказал, что это безопасно.

Сценарий, с включенным кэшем, не занимает заметного времени, поэтому обеспечивает хороший пользовательский опыт. Я надеюсь, что вы можете использовать некоторые из моих идей;)

2 голосов
/ 08 июля 2011

Хорошо, а как насчет другого ( кстати, это тоже немного хаки, )

Когда вы перейдете на страницу галереи, вы будете знать, какие изображения нужно будет отправить пользователю. Я предполагаю, что у них есть идентификатор (имя файла / id / что угодно). Создайте список всех файлов, необходимых для этого запроса, и сохраните его в месте, легко доступном для ваших сценариев, например, в текстовом файле на сервере (недоступном для общественности). Дайте этому файлу идентификатор.

Теперь, есть еще один PHP-файл, который не загружает полный Zend Framework. Он должен получить некоторые параметры из URL: запрашиваемый файл изображения и идентификатор списка.

Запишите URL-адреса в теги img примерно так:

<img src="image.php?file=myFile.jpg&list=12345" />

Файл image.php должен открыть список с этим идентификатором и проверить, существует ли в нем myFile.jpg. Если да, покажите его, если не выдайте 404.

И не забывайте регулярно очищать старые списки.

2 голосов
/ 08 июля 2011

как насчет размещения всех изображений в общедоступном каталоге, настроенного на отсутствие списка каталогов, и использования очень длинных и случайных имен файлов (сгенерированных, например, с помощью md5 из идентификатора и некоторой соли), которые отслеживаются в базе данных, иимя файла меняется после каждого доступа.

База данных содержит ... имя файла: 'myfile.jpg', имя_папки: 'uysdfnasdufhansvdufgnvasoeuvncas.jpg'

Функция доступа ищет имя базы данных и соответственно вставляет URL

После доступа имя файла остается прежним, имя временного файла изменяется и файл переименовывается в новое имя временного характера

1 голос
/ 08 июля 2011

Каковы ваши соображения безопасности?Какая у вас модель угрозы?Как эти решения решают эту проблему?

Ни одно из решений не решает проблему пиявки.Ни одно из решений не касается перераспределения вредоносных программ.Ни одно из решений (как представлено) не ограничивает доступ к элементам контента.

Решение вашего партнера по команде имеет недостатки в том, что оно загружает все изображение в память PHP без необходимости.

Ваше решение не очень эффективно, поскольку требует2 операции чтения и записи вместо одного чтения, и вы только сокращаете окно возможностей для неограниченного доступа к контенту.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...