Гид по технологиям

Защита модулей загрузки файлов от атак клиентской подделки

8 min read Веб‑безопасность Обновлено 08 Jan 2026
Защита загрузок файлов от клиентской подделки
Защита загрузок файлов от клиентской подделки

Сравнение кода на ноутбуке и ПК

Файловые загрузки остаются одной из самых уязвимых частей веб-приложений. Даже «небольшие» ошибки в обработке загруженных файлов часто приводят к удалённому выполнению кода на сервере или краху безопасности проекта. Разработчикам нужно понимать распространённые ошибки и возможные сценарии атак, чтобы строить надёжные защитные механизмы.

Что такое клиентская подделка?

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

  • имя загруженного файла;
  • значение заголовка Content-Type (MIME-типа), отправляемое браузером или клиентом.

Оба этих значения полностью управляются клиентом и могут быть подделаны. Имя файла может содержать любые символы и несколько точек; Content-Type легко изменить в запросе. Это даёт злоумышленнику пространство для обхода примитивных проверок на стороне сервера.

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

Расширение файла и белые списки

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

Первый технический барьер при обработке загрузок — белый список допустимых расширений. Но простая проверка расширения по имени файла ненадёжна. Проблемные сценарии:

  • имя «muo.jpeg.php» обходит простые проверки по последней точке;
  • регистр расширения может отличаться (.JPEG, .Jpg);
  • расширение отсутствует вовсе.

Пример простого PHP-кода из исходного текста (сохранён):

$file_parts = pathinfo($filename);  
switch($file_parts['extension'])  
{  
    case "jpg":  
    break;  
  
    case "bat": // Or exe, dll, so, etc.  
    break;  
  
    case "":  
    case NULL: // No file extension  
    break;  
}  

Этот фрагмент демонстрирует идею, но требует доработки. Рекомендации:

  • Приводите расширение к нижнему регистру и сравнивайте со списком разрешённых расширений.
  • Не доверяйте только имени — проверяйте содержимое файла (finfo или аналог).
  • Генерируйте безопасные имена файлов на сервере (например, UUID или хэш).
  • Скрывайте структуру и расположение директории загрузки.

Пример более надёжной логики на PHP (иллюстрация):

// Пример проверки расширения и содержимого
$allowed_ext = ['jpg','jpeg','png','gif'];
$original_name = $_FILES['file']['name'];
$ext = strtolower(pathinfo($original_name, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_ext)) {
    throw new Exception('Недопустимое расширение');
}
// Проверяем MIME по содержимому
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if (!in_array($mime, ['image/jpeg','image/png','image/gif'])) {
    throw new Exception('Недопустимый MIME-тип');
}
// Безопасное имя
$safe_name = bin2hex(random_bytes(16)) . '.' . $ext;
move_uploaded_file($_FILES['file']['tmp_name'], '/var/www/uploads/' . $safe_name);

Дополнительно: при работе с изображениями полезно принудительно пересоздавать изображение на сервере (например, с помощью GD или ImageMagick). Это исключает возможность загрузки полиглот-файла, который одновременно является изображением и исполняемым кодом.

Что такое информация Content-Type?

Content-Type — это MIME-тип, который браузер указывает при отправке файла в HTTP-запросе. Этот заголовок помогает серверу понять, с каким типом содержимого он имеет дело. Однако значение Content-Type приходит от клиента и может быть подделано.

Почему Content-Type нельзя доверять:

  • злоумышленник может изменить заголовок или сформировать сырой запрос с ложным MIME;
  • некоторые клиенты или прокси выполняют «MIME-sniffing» и могут интерпретировать контент по-своему.

Что нужно делать:

  1. Всегда проверяйте MIME по фактическому содержимому файла (finfo, getimagesize для изображений).
  2. Устанавливайте заголовок X-Content-Type-Options: nosniff на стороне сервера, чтобы браузеры не «угадывали» MIME и не интерпретировали контент иначе.
  3. При возможности пересоздавайте/пересохраняйте файлы на сервере (ревалидация), чтобы удалить скрытые данные и полезные нагрузки.

Пример проверки MIME в PHP (фрагмент выше) и вызов getimagesize для изображений:

$info = @getimagesize($_FILES['file']['tmp_name']);
if ($info === false) {
    throw new Exception('Файл не является изображением');
}
// $info[2] содержит константу IMAGETYPE_..., можно дополнительно проверить соответствие расширению

Также важно логировать несоответствия MIME и расширения для последующего анализа инцидентов.

SWF (Flash) и последовательность атаки

Поддержка Adobe Flash официально завершена, но на некоторых системах и браузерах всё ещё могут встречаться плагины или встроенные реализации. Flash/SWF-файлы могут быть исполнены браузером с типом application/x-shockwave-flash, даже если расширение файла выглядит как .jpeg, если браузер или плагин принудительно интерпретирует файл как SWF.

Типичный сценарий атаки:

  1. Злоумышленник загружает SWF-файл с именем image.jpeg. Сервер пропускает файл по расширению, а Content-Type был подделан клиентом.
  2. Файл оказывается по URL: https://www.target-site.com/images/image.jpeg.
  3. На сайте attacker.com злоумышленник встраивает этот URL в с type=”application/x-shockwave-flash”.
  4. Посетитель attacker.com получает страницу, браузер загружает SWF с сайта target-site и выполняет его. Если SWF управляет запросами, он может отправлять авторизованные запросы к target-site от имени залогиненного пользователя, обходя ограничения (например, CSRF).
  5. Оригинальный фрагмент HTML из сценария атаки (сохранён):

    style="height:1px;width:1px;" data="www.target-site.com/images/image.jpeg" type="application/x-shockwave-flash" allowscriptaccess="always" flashvars="c=read&u=somethings"  

    Меры противодействия:

    • Храните загруженные файлы на отдельном поддомене, например: https://file.target-site.com/images/image.jpeg. Отдельный поддомен можно настроить так, чтобы он не отправлял пользовательские cookie для основного домена (set-cookie domain) и имел отдельную политику CORS.
    • Отдавайте файлы с заголовком Content-Disposition: attachment; filename=”…” — тогда браузер предложит скачать файл, а не исполнит его встроенно.
    • Устанавливайте X-Content-Type-Options: nosniff, чтобы запретить «угадать» MIME.
    • Отключите поддержку плагинов и параметров, таких как allowscriptaccess, на страницах, где не нужен сторонний функционал.
    • По возможности полностью запретите хранение исполняемых форматов (SWF, HTML, PHP, EXE) в директориях публичной отдачи.

    Практические рекомендации — чеклист и SOP для команд

    Ниже приведён чеклист ролей и минимальный план действий для внедрения безопасной обработки файлов.

    Чеклист для разработчика

    • Использовать белый список расширений и сравнивать в нижнем регистре.
    • Проверять MIME по содержимому (finfo, getimagesize).
    • Генерировать безопасные серверные имена файлов.
    • Пересоздавать изображения на сервере (ревалидация/ребилд).
    • Ограничивать максимальный размер загружаемых файлов.
    • Устанавливать строгие права доступа на каталоги загрузки (chmod 0700/0750).
    • Логировать аномалии и блокированные попытки.

    Чеклист для DevOps/инфраструктуры

    • Отдавать статические файлы с отдельного поддомена или через объектное хранилище (S3, GCS) без cookie основного сайта.
    • Добавлять заголовки безопасности: X-Content-Type-Options: nosniff, Content-Security-Policy, Strict-Transport-Security.
    • Конфигурировать веб-сервер так, чтобы файлы в uploads не могли исполняться (deny exec, не подключать PHP в каталоге загрузок).
    • Настроить сканирование загруженных файлов антивирусом/антивредоносом.

    Чеклист для QA/безопасности

    • Писать тесты на обходы белых списков и проверять поведение при двойных расширениях.
    • Проводить fuzz-тестирование загрузок.
    • Проверять реакции сервера на полиглот-файлы и файлы с некорректным MIME.

    SOP при обнаружении уязвимости загрузки файла

    1. Немедленно вынести каталог загрузок из публичной зоны (сделать файлы недоступными по URL).
    2. Проанализировать логи и найти все подозрительные загрузки.
    3. Удалить/изолировать подозрительные файлы.
    4. Закрыть уязвимый код/патч и запустить регрессионные тесты.
    5. Провести ретроспективу и обновить чеклисты.

    Критерии приёмки

    • Все загружаемые файлы проходят проверку расширения и содержимого.
    • Файлы хранятся под безопасными именами и с правами 0600/0640.
    • Отдача файлов не осуществляется с основного домена и не использует cookie.
    • Логи фиксируют любые несоответствия и превышения лимитов.

    Когда базовые механизмы не сработают — сценарии обхода

    • Полиглот-файлы: файл может содержать валидные данные для двух форматов одновременно (напр., изображение + встраиваемый скрипт).
    • Серверная ошибка конфигурации: если PHP выполняется в директории uploads, даже безопасно называемый файл может исполниться.
    • Недостаточная валидация на стороне клиента и отсутствие контроля на сервере.
    • Старые браузеры/плагины могут интерпретировать контент иначе и выполнять его.

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

    Альтернативные подходы и дополнительные меры

    • Хранение в объектном хранилище (S3/GCS) с настроенными политиками доступа и «безопасной» отдачей через CDN.
    • Пересоздание изображения: загрузка → серверный ресайз/кодирование → сохранение нового файла, что устраняет вредоносные вставки.
    • Антивирусное сканирование загружаемых файлов.
    • Ограничение диапазона допустимого контента: если нужен только аватар, разрешать только небольшие размером файлы с жёстким ресайзом.
    • Замена исполняемых форматов на безопасные представления (например, превью вместо оригинала).

    Примеры конфигураций и сниппеты

    Nginx: разрешаем отдачу, запрещаем исполнение и добавляем заголовки безопасности

    server {
        listen 443 ssl;
        server_name file.target-site.com;
    
        root /var/www/uploads;
    
        location / {
            # Не обрабатываем PHP в этом каталоге
            location ~* \.(php|phtml)$ {
                deny all;
            }
    
            add_header X-Content-Type-Options nosniff;
            add_header Content-Security-Policy "default-src 'none';";
            add_header X-Frame-Options DENY;
        }
    }

    HTTP-ответ: принудительное скачивание

    HTTP/1.1 200 OK
    Content-Disposition: attachment; filename="image.jpeg"
    Content-Type: application/octet-stream

    Примечание: Content-Disposition: attachment уменьшит шанс, что браузер исполнит файл встроенно.

    Тесты и критерии приёмки для автоматизации

    Тестовые кейсы:

    • Загрузка файла image.jpeg с реальным JPEG-содержимым — должен пройти.
    • Загрузка файла image.jpeg, но содержащего SWF/EXE — должен быть отклонён/помечен.
    • Попытка загрузки файла с двойным расширением muo.jpeg.php — отклонена.
    • Попытка доступа к загруженному файлу не должна отдавать cookie домена основного сайта.
    • Проверка заголовков ответа: X-Content-Type-Options присутствует; Content-Disposition — attachment для неподтверждённых форматов.

    Критерии приёмки (повтор): все тесты должны пройти в CI и регрессия не должна возвращать уязвимости загрузки.

    Уровни зрелости защиты загрузок

    • Уровень 1 (базовый): проверка расширения по имени и ограничение размера.
    • Уровень 2 (средний): проверка MIME по содержимому, логирование, безопасное имя.
    • Уровень 3 (высокий): пересоздание файлов, антивирус, отдельный поддомен, строгие заголовки.
    • Уровень 4 (превосходный): автоматическое сканирование на полиглот-структуры, honeypot-файлы, блокировка по репутации IP и интеграция в процесс инцидент-менеджмента.

    Конфиденциальность и соответствие требованиям

    Если загружаются персональные данные, убедитесь, что вы соблюдаете требования конфиденциальности и хранения данных (например, GDPR). Основные правила:

    • Уведомляйте пользователя об использовании хранилища и целях обработки.
    • Ограничивайте доступ к файлам по принципу наименьших привилегий.
    • Архивируйте и удаляйте файлы в соответствии с политикой хранения данных.

    Краткая сводка и выводы

    • Никогда не доверяйте имени файла и заголовку Content-Type, присланным клиентом.
    • Всегда проверяйте содержимое файла на сервере (finfo, getimagesize), используйте белые списки и пересоздание файлов.
    • Изолируйте хранилище файлов: отдельный поддомен или объектное хранилище, заголовки безопасности и Content-Disposition: attachment.
    • Внедрите роли и процессы: разработчики, DevOps и безопасность должны иметь чёткие обязанности и тесты.

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

    Уведомление об окончании поддержки плагина

    Сводка — что делать в первую очередь:

    1. Добавьте проверку содержимого (finfo/getimagesize).
    2. Используйте белые списки расширений и безопасные имена.
    3. Храните файлы на отдельном поддомене и добавляйте X-Content-Type-Options: nosniff.
    4. Пересоздавайте/обрабатывайте файлы перед публикацией и сканируйте на вредоносный код.

    Конец статьи.

    Поделиться: X/Twitter Facebook LinkedIn Telegram
    Автор
    Редакция

    Похожие материалы

    RDP: полный гид по настройке и безопасности
    Инфраструктура

    RDP: полный гид по настройке и безопасности

    Android как клавиатура и трекпад для Windows
    Гайды

    Android как клавиатура и трекпад для Windows

    Советы и приёмы для работы с PDF
    Документы

    Советы и приёмы для работы с PDF

    Calibration в Lightroom Classic: как и когда использовать
    Фото

    Calibration в Lightroom Classic: как и когда использовать

    Отключить Siri Suggestions на iPhone
    iOS

    Отключить Siri Suggestions на iPhone

    Рисование таблиц в Microsoft Word — руководство
    Office

    Рисование таблиц в Microsoft Word — руководство