Какой самый безопасный способ загрузки файла? - PullRequest
35 голосов
/ 02 ноября 2008

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

Я пытаюсь настроить серию сценариев загрузки типа «включай и работай», чтобы использовать их внутри компании, чтобы дизайнер мог скопировать в структуру своего сайта, изменить несколько переменных и получить готовый к работе загрузить форму на свой сайт. Мы стремимся максимально ограничить нашу экспозицию (мы уже отключили команды fopen и shell).

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

Редактировать: Я нашел свой ответ (опубликован ниже) и, хотя он использует команду оболочки exec (), если вы блокируете загрузку файлов скриптов (что это решение делает очень хорошо), у вас не возникнет никаких проблем.

Ответы [ 3 ]

66 голосов
/ 30 августа 2013
  1. Разрешить загружать файл только авторизованным пользователям. Вы также можете добавить капчу, чтобы препятствовать примитивным ботам.

  2. Прежде всего, установите MAX_FILE_SIZE в форме загрузки , а также установите максимальный файл size и count на сервере .

    ini_set('post_max_size', '40M'); //or bigger by multiple files
    ini_set('upload_max_filesize', '40M');
    ini_set('max_file_uploads', 10);
    

    Проверка размера по загруженным файлам:

    if ($fileInput['size'] > $sizeLimit)
        ; //handle size error here
    
  3. Вы должны использовать $_FILES и move_uploaded_file(), чтобы поместить свои загруженные файлы в правильный каталог, или, если вы хотите обработать его, проверьте с помощью is_uploaded_file(). (Эти функции существуют для предотвращения инъекций имен файлов , вызванных register_globals.)

    $uploadStoragePath = '/file_storage';
    $fileInput = $_FILES['image'];
    
    if ($fileInput['error'] != UPLOAD_ERR_OK)
        ; //handle upload error here, see http://php.net/manual/en/features.file-upload.errors.php
    
    //size check here
    
    $temporaryName = $fileInput['tmp_name'];
    $extension = pathinfo($fileInput['name'], PATHINFO_EXTENSION);
    
    //mime check, chmod, etc. here
    
    $name = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); //true random id
    
    move_uploaded_file($temporaryName, $uploadStoragePath.'/'.$name.'.'.$extension);
    

    Всегда генерировать случайный идентификатор вместо использования исходного имени файла .

  4. Создайте новый поддомен , например http://static.example.com или, по крайней мере, новый каталог за пределами public_html для загруженных файлов. Этот поддомен или каталог не должен выполнять ни один файл . Установите его в конфигурации сервера или установите в .htaccess файле в каталоге.

        SetHandler none
        SetHandler default-handler
        Options -ExecCGI
        php_flag engine off
    

    Установите с помощью chmod().

        $noExecMode = 0644;
        chmod($uploadedFile, $noExecMode);
    

    Используйте chmod() и для вновь загруженных файлов и установите его в каталоге.

  5. Вы должны проверить тип пантомимы , отправленный хакером. Вы должны создать белый список разрешенных типов пантомимы . Разрешить только изображения , если не нужен какой-либо другой формат. Любой другой формат представляет собой угрозу безопасности. Изображения тоже, но, по крайней мере, у нас есть инструменты для их обработки ...
    поврежденное содержимое , например: HTML в файле изображения может вызывать XSS в браузерах с анализом содержимого уязвимостью, Если поврежденный контент представляет собой код PHP , его можно объединить с уязвимостью evaljection .

    $userContent = '../uploads/malicious.jpg';
    include('includes/'.$userContent);
    

    Попытайтесь избежать этого, например, используйте class autoloader вместо того, чтобы включать файлы php вручную ...
    При обработке javascript-инъекции сначала вам нужно отключить xss и прослушивание контента в браузерах . Обнюхивание контента проблемы типичны для старых msie , я думаю, что другие браузеры фильтруют их довольно хорошо. В любом случае вы можете предотвратить эти проблемы с помощью нескольких заголовков. (Не полностью поддерживается каждым браузером, но это лучшее, что вы можете сделать на стороне клиента.)

    Strict-Transport-Security: max-age={your-max-age}
    X-Content-Type-Options: nosniff
    X-Frame-Options: deny
    X-XSS-Protection: 1; mode=block
    Content-Security-Policy: {your-security-policy}
    

    Вы можете проверить, поврежден ли файл с помощью Imagick identify, но это не означает полную защиту.

    try {
        $uploadedImage = new Imagick($uploadedFile);
        $attributes = $uploadedImage->identifyImage();
        $format = $image->getImageFormat();
        var_dump($attributes, $format);
    } catch (ImagickException $exception) {
        //handle damaged or corrupted images
    }
    

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

    X-Download-Options: noopen
    Content-Disposition: attachment; filename=untrustedfile.html
    
  6. Можно иметь действительные файлы изображений с кодом внутри них, например, в данных exif . Поэтому вам нужно очистить exif от изображений , если его содержание не важно для вас. Вы можете сделать это с помощью Imagick или GD, но оба они требуют перепаковки файла. Вы можете найти exiftool в качестве альтернативы. Я думаю, что самый простой способ очистить exif , это загрузить изображения с GD , и сохранить их как PNG с высочайшим качеством . Таким образом, изображения не будут терять качество, а тег exif будет очищен, потому что GD не может справиться с этим. Сделайте это с изображениями, загруженными как PNG тоже ...
    Если вы хотите извлечь данные exif , никогда не используйте preg_replace(), если pattern или replacement от пользователя, потому что это приведет к eval инъекции .. При необходимости используйте preg_replace_callback() вместо eval regex flag. (Распространенная ошибка в копировании и вставке кодов.) Exif данные могут быть проблемой, если ваш сайт имеет уязвимость evaljection , например, если вы где-то используете include($userInput).

  7. Никогда не используйте include(), require() загруженными файлами , используйте их как статические или используйте file_get_contents() или readfile(), или любую другую функцию чтения файлов, если хотите контролировать доступ.
    Он редко доступен, но я думаю, что лучше всего использовать заголовки X-Sendfile: {filename} с модулем sendfile apache . По заголовкам, никогда не используйте пользовательский ввод без проверки или очистки, потому что это приведет к инъекции HTTP-заголовка .
    Если вам не нужен контроль доступа (что означает, что только авторизованные пользователи могут видеть загруженные файлы), предоставьте файлы вашему веб-серверу. Это намного быстрее ...

  8. Используйте антивирь для проверки загруженных файлов, если он у вас есть.

  9. Всегда используйте комбинированную защиту , а не просто один подход. Будет сложнее нарушить вашу защиту ...

37 голосов
/ 02 ноября 2008

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

1 голос
/ 02 ноября 2008

Использовать и настраивать Hardened-PHP создать простой скрипт, используя move_uploaded_file и суперглобальный $ _ FILES . Самый простой сценарий - самый безопасный (по крайней мере, такой же безопасный, как и сама работающая версия PHP)

...