×Закрыть

Безопасность в вебе, или TrustedTypes как новый способ защиты от XSS

В этой статье я постараюсь вас убедить, что опасность XSS, несмотря на все современные фреймворки, по-прежнему существует. Также мы рассмотрим главные способы защиты. Основное внимание уделим новому революционному подходу — DOM TrustedTypes, который, несмотря на то что еще находится в разработке, обещает поднять безопасность браузеров на новый уровень.

Безопасность в вебе

В далеком 2008 году я окончил университет магистром в области информационной безопасности. К сожалению, ничего интересного по специальности на тот момент не нашлось, и я пошел работать в мир веб-разработки. Имея серьезную теоретическую базу, я всегда представлял защиту информации на уровне криптографических алгоритмов, технических устройств и процессов. Но когда я углубился в веб-разработку, понял, что мир веба развивается очень быстро, а вот культура безопасности серьезно отстает. Это было во всем. Никто не уделял внимания анализу уязвимостей. Этим грешили даже крупные компании. Дыры безопасности можно было найти в порталах с миллионами активных пользователей. Простые поисковые запросы в гугле сразу же выдавали десятки сайтов.

Однако после крупных взломов таких компаний, как MySpace (XSS), eBay (XSRF) и многих других, до компаний начало доходить, что нужно что-то делать.

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

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

В своей статье я хочу сфокусироваться на XSS-атаке, а также рассмотреть новый способ защиты от нее — DOM TrustedTypes.

Почему XSS все еще опасен

XSS (англ. Cross-Site Scripting — «межсайтовый скриптинг») — атака не новая, и большая часть программистов с ней знакомы. Хакер использует уязвимость (как правило, это неотфильтрованный пользовательский ввод данных), чтобы разместить вредоносный код.

С каждым годом фронтенд-приложений становится все больше, и сами приложения становятся все объемнее и сложнее. Поэтому логично, что появляется все больше атак XSS непосредственно в DOM.

Давайте рассмотрим пример. Программисты веб-приложения написали вот такой код:

Чем это может быть опасно? Тем, что мы выводим неотфильтрованные данные прямиком в DOM. Хакер может подкинуть для нашего пользователя-жертвы вот такую ссылку:

При этом в DOM мы получим вредоносный код:

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

Многие считают, что основная цель XSS — стянуть пароль из кукисов, ну а если пароль/токен в кукисах не хранится (а например, в localStorage), то XSS сайту не страшен. Это заблуждение. Кроме стягивания кукисов, c XSS можно совершать много различных манипуляций, например:

Как вы, наверное, догадались — этот код предназначен, чтобы красть номера кредитной карточки.

А вот этот будет заниматься майнингом криптовалюты за счет браузера нашего пользователя:

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

Если вы до этого не сталкивались с такой утилитой, как BeEF — очень рекомендую обратить внимание. Она помогает раскрыть потенциал уязвимости и обеспечивает удобный графический интерфейс.

Еще одно заблуждение, что XSS — проблема прошлого, а сейчас нас защищают современные фреймворки и фаерволы на серверах, что у всех серьезных компаний инфраструктура настроена так, что как бы «криворукие» программисты ни старались, они не смогут создать XSS.

Чтобы развенчать этот миф, достаточно привести пример Google: основная функция — поиск — подверглась XSS в конце прошлого года. А привела к этому уязвимость в библиотеке Closure.

Как защититься от XSS

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

Фильтрация/санитизация данных

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

  • Не доверять никому (с вашего API тоже могут прийти опасные с точки зрения HTML данные, на стороне сервера работают такие же программисты, которые могли забыть что-то отфильтровать).
  • Не пытайтесь написать санитизатор самостоятельно, вы не умнее десятков тысяч хакеров, которые пытаются обойти все варианты фильтрации. Уже существует множество зрелых решений для любой технологии.
  • Общая санитизация не подходит, если мы не знаем тип контента, который собираемся отфильтровать (ведь для URL и для HTML опасными будут разные кодовые комбинации).

Использование CSP-заголовков

Content Security Policies (или просто CPS) — очень хороший способ защиты от XSS, когда у вас огромный проект, и вы не уверены, что знаете все его места. CSP позволяет будто зонтиком «накрыть» сразу весь проект, и даже плохой код с уязвимостями и неотфильтрованными данными.

Как работает CSP

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

То есть в теории если мы знаем, что у нас нет инлайн скриптов, мы можем их просто запретить, и если хакер сможет внедрить опасный код из примера выше:

Он просто не выполнится в браузере.

Включение CPS в режиме отчета

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

CSP в реальной жизни

Мы на своем проекте принципиально отказались от inline-скриптов, и даже код-сниппеты, такие как Google Analytics, перенесли в отдельные файлы. Это, я думаю, первое, что вам необходимо сделать, чтобы предотвратить возможную XSS-атаку.

Далее прошлись по всем сторонним ресурсам, которые используются приложением, и добавили их в исключения. Не со всеми сервисами 3rd Party это получается просто: они подгружают дополнительные файлы динамически, то есть при обновлении библиотеки это уже может быть другой файл.

Проблемным также оказался вопрос с фреймами: некоторые клиенты используют наш портал внутри тега iframe.

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

Более подробно о CSP можно почитать в MDN-документации.

Использование Trusted Types в DOM

Если мы копнем глубже — из-за чего происходят все проблемы — то окажется, что мы просто доверяем браузеру делать слишком много магии, при этом не задавая контент для выполнения. Самый распространенный случай — innerHTML= ‘.......’ То есть мы передаем строку, в которой может быть все что угодно, в том числе вредоносный скрипт.

При этом вы можете работать с innerHTML не напрямую — это может делать одна из ваших вспомогательных библиотек (так, например, делала jQuery в методе append).

Так, может, нам просто заблокировать innerHTML на глобальном уровне? Нет, увы: innerHTML — не единственное проблемное место в DOM, есть много свойств-методов, которые делают примерно такую же магию с передаваемой строкой:

Но что, если бы существовала возможность передавать значение не как строка, а как объект? Ведь DOM уже давно поддерживает такую возможность:

При этом этот объект знал бы свой скоуп, то есть либо это URL, либо это HTML, либо еще что-то. В зависимости от этого браузер или сам объект мог бы валидировать/фильтровать данные, ничего не ломая.

Веб-платформа либо полифилы предоставляет набор готовых типов:

  • TrustedHTML
  • TrustedScript
  • TrustedURL
  • TrustedScriptURL

Но при этом мы можем добавлять свои в случае необходимости.

Как подключить Trusted Types

Для подключения Trusted Types необходимо лишь добавить в CSP дополнительную инструкцию:

Content-Security-Policy: trusted-types *

И теперь, если мы попытаемся сделать что-то такое в коде:

Браузер просто выдаст ошибку:

Uncaught TypeError: Failed to set the ’innerHTML’ property on ’Element’: This document requires `TrustedHTML` assignment.

Но если мы сделаем присвоение через специальный объект:

Все отработает правильно!

Плюс ко всему, мы еще можем лимитировать возможные варианты политик: добавим вместо звездочки имя политики (класса), которую используем:

Content-Security-Policy: trusted-types myPolicy

Несмотря на то что этот проект все еще остается в формате Draft, вы уже можете поиграться в Chrome 73+, используя специальный флаг:

chrome --enable-blink-features=TrustedDOMTypes

TrustedTypes в реальной жизни

Даже при том, что DOM TrustedTypes по-прежнему является экспериментальной функцией, если вы хотите в последующем (с принятием стандарта) активировать его на проекте — изменения нужно начинать готовить уже сейчас.

Мы на своем проекте используем Angular, который уже частично включает концепцию TrustedTypes: если правильно использовать фреймворк, то он будет контролировать всю передачу и отображение данных в шаблоне, при этом Angular четко знает тип передаваемых данных и экранирует их в зависимости от типа. Более того, уже даже есть Pull Request добавить TrustedTypes нативно во фреймворк.

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

LinkedIn

5 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

fix typo in the first code snippet:
's/location.gref/location.href/'

Спасибо за статью, не слышал до этого про, trusted types интересно было узнать.

Мы на своем проекте принципиально отказались от inline-скриптов, и даже код-сниппеты, такие как Google Analytics, перенесли в отдельные файлы

Этого не нужно делать. Можно воспользоваться для таких скриптов хешом или nonce. nonce будет удобнее, если контент динамический (чексумма меняется и хеш статический не привязать). Тут подробнее: www.troyhunt.com/...​es-nonces-and-report-uri

Ну а вообще, всякие аналитики и Google Tag Manager может подключать (c сохранение CSP), чтобы не дергать разработчиков и не деплоить ради новой аналитики или трекинг ивентов приложение.

хешом или nonce

спасибо за направление мысли, подумаем

всякие аналитики и Google Tag Manager может подключать

мы и используем GTM, но он изначально тоже идет как инлайн сниппет (в статье я использую пример с аналитикой, так как не все знают GTM). Но использование GTM не избавляет вас о переписывания CSP, если нужно добавлять новый домен в список разрешенных

Подписаться на комментарии