Проблема:

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

Это приложение Java / Spring. У меня есть то преимущество, что мне нужно беспокоиться только о IE11 и Firefox v38 + (было бы неплохо иметь Chrome v43 +)

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

 <script>alert("malicious code here!")</script>

И сохраните его как «malwareImage.jpg» и загрузите его.

Позже, когда это изображение отображается внутри тегов изображения, таких как:

 <img src="blah?imgName=foobar" id="someImageID">

ActualImage.jpg отображается нормально, а malwareImage.jpg отображается как неработающая ссылка - и что самое важное, никакой вредоносный контент не интерпретируется!

Однако Если пользователь щелкает правой кнопкой мыши по этой неработающей ссылке и нажимает кнопку «просмотреть изображение» ... случаются плохие вещи.

Браузер анализирует содержание, что является для меня новой концепцией, обнаруживает, что «malwareImage.jpg» на самом деле является текстовым файлом, и очень любезно представляет его как HTML без колебаний. Любые теги сценария передаются интерпретатору JavaScript, и, как вы можете себе представить, мы этого не хотим.

Что я пробовал до сих пор

Короче говоря, любая возможная комбинация заголовков ответов, которую я могу придумать, чтобы предотвратить перехват содержимого браузером. Все ответы, которые я нашел здесь на stackoverflow и других документах, подразумевают, что установка заголовка типа контента должна препятствовать большинству браузеров перехватывать контент, а установка опций X-контента должна предотвращать некоторые версии IE.

Я устанавливаю параметры x-content-type-options без сниффа, и я устанавливаю тип содержимого ответа. Документы, которые я прочитал, наводят меня на мысль, что это должно прекратить прослушивание контента.

response.setHeader("X-Content-Type-Options", "nosniff"); 
response.setContentType("image/jpg");

Я перехватываю ответ, и эти заголовки присутствуют, но, похоже, не влияют на обработку вредоносного контента ...

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

Конечная цель:

Естественно - любой вывод для изображений, которые на самом деле не являются изображениями (искаженная ерунда, необработанное исключение и т. Д.), Был бы лучше, чем выполнение текстового файла в виде HTML / javascript в открытом виде, но отображение любого вредоносного HTML в виде экранированного / CDATA простой текст был бы идеальным ... хотя, возможно, немного непрактичным.

4
Paul 15 Дек 2015 в 14:58

3 ответа

Лучший ответ

Поэтому я решил эту проблему, но забыл ответить на свой вопрос:

Шаг 1: блокировка недействительных изображений

Чтобы быстро это исправить, я просто добавил довольно грубый код, который проверял, является ли изображение фактически изображением - во время загрузки и перед его обслуживанием, используя imageio lib:

import javax.imageio.ImageIO;

//...... 

Image img = attBO.getImage(imgId);

InputStream x = new ByteArrayInputStream(img.getData());
BufferedImage s;
try {
    s = ImageIO.read(x);
    s.getWidth();
} catch (Exception e) {
    throw new myCustomException("Invalid image");
}

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

Хотя это будет блокировать:

 <script>alert("malicious code here!")</script>

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

Шаг 2: рамки глупости

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

Это означало, что, хотя контроллер явно возвращал image / png, он захватывался и помещался (в байтах), постобработка брала этот байтпоток и помещала его в верхний и нижний колонтитулы, чтобы сформировать полностью квалифицированное «представление» - это view всегда имел бы текст / html типа контента и, следовательно, никогда не отображался правильно.

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

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

Например, с помощью site-mesh это было просто исключение (как всегда, простое исправление, как только я понял проблему ...):

<decorators defaultdir="/WEB-INF/decorators">
<excludes>
    <pattern>*blah.ctl*</pattern>
</excludes>
<decorator name="foo" page="myDecorator.jsp">
    <pattern>*</pattern>
</decorator>

И затем некоторые другие сделанные на заказ перехватчики после вызова.

Шаг 3: Согласование контента

Теперь я, наконец, достиг стадии, когда обслуживался только байт-код изображения, и никакой обзор не был задан или явно сгенерирован.

Запущена функция Spring, называемая «согласование содержимого». Она пытается согласовать заголовок «принимает» запроса с «конвертерами сообщений», которые имеются в наличии для получения таких ответов.

Поскольку у Spring по умолчанию нет конвертера сообщений, который генерировал бы ответы image / png, он возвращался к text / html - и я все еще видел проблемы.

Теперь, если бы я использовал Spring 4, я мог бы просто добавить аннотацию:

@Produces("image/png")

На мой контроллер - просто исправить ...

Шаг 4: Устаревшие зависимости

но , потому что у меня была только весна 3.0.5 (и я не мог ее обновить), мне пришлось попробовать другие вещи.

Я пытался зарегистрировать новые конвертеры сообщений, но это была головная боль или добавление нового перехватчика после метода, чтобы просто изменить тип контента обратно на 'image / png' - но это была хакерская головная боль.

В конце я просто выставил запрос / ответ в контроллере и записал свое изображение непосредственно в тело ответа - вообще обойдя согласование содержимого Spring.

.... и наконец мое изображение служило изображением и отображалось как изображение - и не было выполнено никакого введенного кода!

1
Paul 14 Апр 2016 в 11:26

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

-1
Tom 15 Дек 2015 в 12:49

Это звучит странно, потому что это прекрасно работает в других местах. Вы уверены, что заголовок X-Content-Type-Options присутствует в ответах?

Вот демонстрация, которую я построил некоторое время назад, где у меня есть файл, который является действительным HTML, GIF и JavaScript. Как вы можете видеть, он сначала загружается как HTML, но затем загружается как изображение и как скрипт (который выполняется):
http://research.insecurelabs.org/content-sniffing/gifjs.html

Однако, если вы загрузите его с помощью заголовка «X-Content-Type-Options: nosniff», скрипт больше не будет выполняться:
http://research.insecurelabs.org/content-sniffing/nosniff/gifjs.html

Между тем изображение отображается правильно в FF / IE, но не в Chrome.

Вот демонстрация, где я попробовал то, что вы описали:
http://research.insecurelabs.org/content-sniffing/stackexchange.html

Первое изображение без nosniff, а второе с, и, кажется, работает как задумано. Второй не запускает скрипт при открытии с помощью «view image».

< Сильный > Edit:
Firefox, похоже, не поддерживает X-Content-Type-Options: nosniff

Таким образом, вы также должны добавить «Content-disposition: attachment; filename = image.gif» или похожие на изображения. Изображение будет загружаться нормально, если оно загружено через тег изображения, но если вы откроете URL-адрес напрямую, вы начнете загрузку вместо того, чтобы показывать изображение непосредственно в браузере.

Пример: http://research.insecurelabs.org/content-sniffing/attachment/

0
Erlend 16 Дек 2015 в 13:47