Тестировщик обнаружил, что endpoint с пользовательскими файлами не задаёт заголовок X-Content-Type-Options. Как проверить риск MIME-sniffing?
Нужно отправить файл с содержимым, не соответствующим заявленному MIME-типу, и проверить, не интерпретирует ли браузер его как активный контент. Защита считается корректной, если сервер указывает точный Content-Type, добавляет X-Content-Type-Options: nosniff, а пользовательские файлы при необходимости выдаются как вложения.
Само отсутствие nosniff не доказывает уязвимость: риск зависит от контекста загрузки, фактического содержимого, заголовков ответа и поведения конкретного браузера.
MIME-sniffing появился как механизм обратной совместимости: браузеры пытались определить тип ресурса по содержимому, когда сервер указывал неверный или слишком общий Content-Type. Это помогало отображать старые сайты, но позволяло ошибочно воспринимать пользовательский файл как HTML, JavaScript или другой активный ресурс.
Заголовок X-Content-Type-Options: nosniff появился как способ запретить браузеру самостоятельно угадывать тип в критичных контекстах. Он дополняет корректную настройку типов, но не заменяет её.
Если загруженный пользователем файл возвращается с неточным типом и открывается inline, браузер может обработать его не так, как ожидал сервер. Например, содержимое, считавшееся обычным текстом или изображением, в определённом контексте может стать исполняемым или интерпретируемым.
Последствия зависят от места выдачи файла и его связи с приложением: возможны выполнение сценария в доверенном origin, кража данных из доступного контекста или обход предположений о безопасности загрузок. Однако один только «подозрительный» тип ответа ещё не означает эксплуатацию.
Тест следует построить на контролируемом безвредном файле, содержимое которого намеренно не соответствует заявленному типу. Нужно проверить выдачу файла непосредственно в браузере, его загрузку как ресурса и встраивание в релевантные контексты, после чего сравнить результат в поддерживаемых браузерах.
Проверяются как минимум следующие свойства:
Content-Type, соответствующий разрешённому формату;X-Content-Type-Options: nosniff;Content-Disposition: attachment;Минимальный пример безопасной части ответа:
nosniff особенно важен для проверок типа в контекстах скриптов и таблиц стилей, но не является универсальным фильтром содержимого. Он не санитизирует HTML или SVG, не исправляет ошибочный Content-Type и не защищает от логики приложения, которая сама небезопасно встраивает файл.
Поэтому результат формулируют не как «заголовок отсутствует — уязвимость есть», а как доказательство конкретного опасного поведения: браузер интерпретирует контролируемое содержимое в активном контексте при условиях, которые допускает приложение.
Сервис принимает изображения профиля и возвращает их с типом, взятым из имени файла. Тестировщик загрузил безвредный файл с содержимым активного формата, но с расширением изображения, затем проверил непосредственное открытие URL и использование URL в странице.
Рассматривались три варианта. Только добавление nosniff было простым, но не исправляло ошибочный тип и не решало проблемы опасных форматов. Только проверка расширения была ненадёжной, поскольку расширение не подтверждает содержимое. Полная изоляция файлов на отдельном origin снижала последствия, но требовала инфраструктурных изменений.
Выбрали комбинацию: проверять формат по содержимому, разрешать ограниченный набор безопасных форматов, отдавать файлы с серверным Content-Type, добавлять nosniff, а непредусмотренные для inline-просмотра файлы выдавать как вложения. В результате файл с несоответствующим содержимым не интерпретировался как активный ресурс, а проверка не зависела только от имени файла.
1. Достаточно ли добавить X-Content-Type-Options: nosniff?
Нет. Заголовок ограничивает MIME-подсказки браузера в определённых контекстах, но не проверяет байты файла, не очищает пользовательский HTML и не изолирует ресурс от origin приложения. Нужны корректный тип, безопасная политика выдачи и, при необходимости, отдельный origin или принудительная загрузка.
2. Почему проверка только расширения файла не решает проблему?
Расширение является пользовательским именем, а не доказательством формата. Его можно изменить независимо от содержимого, а некоторые форматы допускают сложную интерпретацию или активные элементы. Поэтому расширение может использоваться как предварительный фильтр, но решение должно опираться на фактический формат, безопасное декодирование и контролируемую выдачу.
3. Всегда ли Content-Disposition: attachment полностью устраняет риск?
Нет. Он снижает риск непосредственной интерпретации в браузере, но не исправляет хранение опасного содержимого, ошибки в других endpoint и обработку файла после скачивания. Нужно также проверить отсутствие альтернативного inline-URL, корректность типа, изоляцию origin и сценарии, в которых приложение само вставляет файл в страницу.