(Не)безопасный frontend
Не так давно я выступал на конференции FrontendConf 2015 (РИТ++) с темой данной статьи. И при подготовке доклада начал искать информацию, а кто вообще выступал на данную тему и что есть в Сети на данный момент.Оказалось, что информации совсем немного, более-менее можно было бы отметить доклад mikewest.org/2013/09/frontend-security-frontendconf-2013 от Mike West из компании Google, но какой-то «непентестерский» взгляд и уж совсем мало материала. И www.slideshare.net/eoftedal/web-application-security-in-front-end где тема раскрыта более детально, но выступление 2011 года. А за 4 года технологии и атаки на месте не стояли.
Долго и сложно выбирая темы, что же все-таки рассказать разработчикам фронтендов про безопасность, при этом минимум касаясь бекэнда (местами все-таки это неделимо), получился доклад, а здесь — его текстовый пересказ.
А действительно, о чем тут вообще можно разговаривать? Говоря про взломы и безопасность невольно приходят в голову тезисы — слили базу, получили доступ к выполнению команд ОС на сервере, прочитали чужую переписку. Но это все — server side код. А что ж может «нагородить» фронтэндер? Главная опасность, конечно же, в обходе атакующим SOP — Same Origin Policy, главной политики безопасности браузеров, которая регулирует работу в разных Origin. Но не только, давайте разбираться. Cross Site ScriptingТема XSS изъезжена как только можно, но обычно речь идет о неправильной обработке параметров на серверной стороне (атакующий может встроить свой JS код в страницу и выполнить его, в том числе в браузере жертвы, от имени уязвимого origin, итог — обход SOP). И выделили там 2 вида XSS атак — stored («злобный» js хранится на сервере) и reflected (отдается сервером без сохранения).Ну, а что с фронтендом то? А здесь другой случай — JS злоумышленника не передается на сервер (за некоторыми исключениями, но дело не в них), а встраивается в DOM за счет легитимного JS кода. Отсюда название для третьего типа атак XSS — DOM XSS.
Типичный пример уязвимого JS кода:
document.write («Site is at:» + document.location.href); Теперь открыв страницу по адресу http://victim.com/action#ѕ JS добавит в дерево значение document.location.href, где вставлен «злобный» js. Как можно уже заметить из данного примера для поиска векторов для данного вида атак нам нужно две вещи: на что-то повлиять в браузере пользователя (sources) и как-то выполнить (sinks).Примеры «сорсов» (sources):
document.URL location document.referrer window.name localStorage cookies Примеры «синков» (sinks)
eval document.write (element).innerHTML (element).src setTimeout / setInterval execScript Более подробно ознакомиться с данной темой можно по ссылкам — code.google.com/p/domxsswiki (и взять отсюда регулярку для поиска потенциально уязвимых мест) и habrahabr.ru/company/xakep/blog/189210, статья от моего коллеги Алексея ТюринаА вот пример уязвимого кода (реального) с twitter.com (full story)
(function (g){var a=location.href.split (»#!»)[1]; if (a){g.location=g.HBR=a;}})(window); Пример DOM XSS на twititer.com
Часто бывает, что JS/CSS файлы содержат информацию об инфраструктуре системы.Про javascript Когда проекты становятся совсем большими и вступают различные среды для приложения (test/dev/prod) начинаются «костыли» и определение, в том числе на клиенте, в каком окружении нужно работать.Пример такого реального файла, mail.ru — img.imgsmail.ru/r/webagent/release/loader.js
var dl = (''+document.location), host = document.location.host, branch = 'master', path = 'release/467', base = 'r/webagent/', probability = [ {«branch»: «wa-514», «deprecated»:1} ], lastForcedVersion = '20131126154524', isLocalhost = dl.indexOf ('localhost') != -1, testServer = host.match (/[^.]+\.((?: f|my\.rapira)\d*)\.mail\.ru/), devServer = host.match (/^.+\.dev\.mail\.ru$/), isRapira = testServer && testServer[1].indexOf ('my.rapira') == 0, utf = true,//! window.IS_UTF, domainProps = {}, domain = '//img.imgsmail.ru', login = getUserLogin (), useBranch = (dl.match (/\Wwa_use_branch=([a-z0–9-]*)/i)||[0, false])[1], useLang = (dl.match (/\Wwa_lang=([a-z]{2})/i)||[0, false])[1], useOnce = (dl.match (/\Wbranch=([a-z0–9-]*)/i)||[0, false])[1], appVersion = (dl.match (/\Wwa_appver=([\.0–9]*)/i)||[0, false])[1], usedBranch = branch; Как видно из файла, по регуляркам можно определить домены (и сбрутить валидные?) для тестов и разработки. В данном случае может информация и не очень критичная (если что — mail.ru в курсе), но встречаются порой и такие конструкции: internalDevHOST = '172.16.22.2'; internalProdHOST = '172.16.22.5'; А иногда — и с внешними IP (хотя внутренние тоже могут быть полезны, при SSRF, например).CSS С CSS тоже самое. Проекты растут, разработчики начинают применять различные «сборщики», которые как раз бывают и оставляют «вкусную» информацию, пример — hackerone.com/reports/2221 file\:\/\/\/applications\/hackerone\/releases\/20140221175929\/app\/assets\/stylesheets\/application\/browser-not-supported\.scss file\:\/\/\/applications\/hackerone\/releases\/20140221175929\/app\/assets\/stylesheets\/application\/modules\/add-category\.scss file\:\/\/\/applications\/hackerone\/releases\/20140221175929\/app\/assets\/stylesheets\/application\/modules\/alias-preview\.scss …
Все популярнее становятся различные JS MVC фрейморвки: AngularJS, Knockout, EmberJS и т.д. С ними очень круто, позволяют расширить работу с DOM, создавать новые элементы (), создавать «биндинги» и т.д. Естественно, что-то новое не могло пройти незаметно для мира безопасности.В частности нас интересуют logic-less темплейты
-
{{phone.name}}
{{phone.snippet}}
Так появился проект mustache security — code.google.com/p/mustache-security, про безопасность MVC фреймворков. Уже есть информация про:
VueJS AngularJS CanJS Underscore.js KnockoutJS Ember.js Polymer Ractive.js jQuery JsRender Kendo UI Так давайте вернемся, что плохого то может здесь случится? Возьмем AngularJS. Например бывают ситуации, когда атакующий как раз попадает как раз внутрь этих скобок. И из них как-то надо выполнить свой JS, но это по-умолчанию нельзя. Задача — обойти «песочницу».Пример с AngularJS (1.1.5)
{{ (_=''.sub).call.call ({}[$='constructor'].getOwnPropertyDescriptor (_.__proto__,$).value,0,'alert (1)')() }} Желаемый alert (1) также получен! Так к чему я. Обновление фрейморков важно не только для функционала, но и для безопасности!
Рассматривая данный пункт мы уже начинаем затрагивать немного backend. Но совсем немного.Как обычно устанавливают cookie на серверной стороне? Пример на PHP:
Пример на Python: import Cookie C = Cookie.SimpleCookie () C[«foo»] = «bar» print C И в первом, и во втором случае сервер ответит заголовком:
Set-Cookie: foo=bar Т.е. просто скажет браузеру, установи значение bar ключу foo. Но нужно все обдумать и проверить себя по чеклисту: Указать домен для установки cookies. В примере выше IE поставит cookie и для текущего поддомена, и для всех поддоменов. Т.е. если будет найдена XSS на каком-то «левом» поддомене — данные могут скомпрометированы. Подробнее. Флаг HttpOnly для сессионных значений (phpsessid, например, по дефолту идет без него). Нужно для того, чтобы браузер запретил доступ к этому значению из JS (при XSS снижает риски хищения аккаунта пользователя) Флаг Secure в случае, если сайт работает только по HTTPS и нет никакой нужды отправлять важные значения через небезопасный канал В третьем пункте было сказано про HTTPS. А давайте как раз это и обсудим, ведь направлено это как раз на работу с браузером. С т.ч. перехода это обычно вопросы администрирования сервера — выбор сертификата, настройка вебсервера и когда уже готово — перенаправление с HTTP на HTTPSКак это бывает в жизни. Админ ставит редирект при заходе на сайт по HTTP на HTTPS и все. Но как раз этот момент, пользователь то изначально заходит по HTTP и именно в этот момент можно «дропнуть» редирект на HTTPS и проксировать трафик дальше, так и не дав пользователю HTTPS версию сайта.
Почему люди заходят на сайт также по HTTP? Потому что у всех он в закладках все также по HTTP.
Отвлечемся на кулстори :)
История находится прямо под этой картинкой
Не могу не рассказать про «горячо любимый» VK. Сейчас прошла/идет/набирает обороты волна «угонов» аккаунтов в ВК. Чаще всего это просто взломанные роутеры пользователей (в роутерах уязвимостей хватает) с измененными DNS серверами, на которых адреса ВК — другие, а именно «злобные серверы злобных злоумышленников». Они как раз и пользуются этим трюком, не дают пользователю посетить HTTPS версию сайта, «дропая» редирект, так как у пользователей сайт все еще в закладах по HTTP версии (галка на странице h_ttps://vk.com/settings? act=security все же не принудительный HTTPS).
Так как лечить? Для этого придумали заголовок HSTS
Strict-Transport-Security: max-age=31536000; Он сообщает браузеру, что на этот сайт по HTTP больше ни запросом. Указывается время, в течение которого данное правило работает (и опционально можно задать распространение данного правила на поддомены).А если включить режим параноика и представить ситуацию, что уже при первом посещении сайта трафик перехватывается и пользователь не получит данный заголовок — то можно добавить свой ресурс в HSTS preload list — www.chromium.org/hsts, список ресурсов, на которые браузер всегда будет посещать по HTTPS (т.е. список ресурсов «зашивается» прямо в браузер, расшаривается между браузерами).
Статья довольно большая, не отчаивайтесь :0 Разговаривая про HTML5 не только в контексте новых элементов, но и новых методов API, он (HTML5) принес много полезного и безопасного, в т.ч. по кроссдоменной работе. А чего только не придумывали, и файлики «proxy.php» в корне сервера, и другие ужасные костыли.Window.postMessage () Представим себе, что у нас есть два объекта window (например — текущее окно и window от iframe или после window.open).Страница на домене abc.com отправляет «секретное» сообщение в другое окно
otherWindow.postMessage (message, targetOrigin); Страница на домене xyz.com слушает сообщения и проверяет, откуда они пришли
window.addEventListener («message», receiveMessage, false); function receiveMessage (event) { if (event.origin!== «http://example.org:8080») return; // … } В целом такой вариант верный — указывать origin, куда отправлять (чтобы не отправить в «окно» атакующему), и принимать, проверяя откуда пришло сообщение. Но в реальности так бывает редко, а чаще нужно такое решение для всего проекта (всех поддоменов)
Уязвимый код!
if (message.orgin.indexOf (».example.com»)!=-1) { /* … */ } И порой можно встретить такой уязвимый код. example.com.attacker.com тоже сматчится (домен атакующего). Так что: Проверяем, куда отправляем данные Проверяем, откуда их получили Перепроверяем свои проверки, чтобы не получилось как выше HTTP access control (CORS) И снова про кроссдоменную работу. CORS клевая, модная, безопасная штука в которой сложно накосячить. Очень сложно, но… бывает :)Позволяет также браузеру сообщить, как ему можно работать в обход SOP. При кроссдоменной работе и первом запросе OPTIONS сервер возвращает что-то типа
Access-Control-Allow-Origin: * Или Access-Control-Allow-Origin: example.com В первом случае сообщается, что можно в обход SOP работать с любого домена, во втором — только с example.com. При этом, можно указывать методы, по которым это доступно и т.д. Но по дефолту идентификационные данные (например — cookies) браузер все равно не отправляет, что чаще всего и нужно. Нужно добавить заголовок
Access-Control-Allow-Credentials: true Который сообщит браузеру, что теперь делать кроссдоменные запросы вместе с куками (позволит работать с сессией пользователя). Но! Самое интересное что уже напрашивающийся мисконфиг Allow-Origin: * (любой сайт) + Allow-Credentials вместе не работают. Браузер не будет отправлять cookies и это хорошо (так как в этом случае злоумышленник не сможет выполнять действия от пользователя с сессией). Т.е. данная опция работает только тогда, когда конкретно указаны разрешенные домены.Про «бывает». Разработчик указывает разрешенные домены из GET параметра (занавес).
Web Sockets Затрагивая Web Sockets (WS) прежде всего стоит отметить, что для них существует несколько стандартов с довольно значимыми изменениями. Поэтому здесь довольно общие вещиВ WS нет авторизации (каждый допиливает сам) WS — передают данные в незашифрованном виде. Следует использовать WSS для важных данных Валидацию данных в них никто не отменял, как на сервере (злоумышленник также может писать свои данные в сокет), так и на клиенте Проверка Origin, откуда данные пришли, не отменяет п.1 … Здесь я уже просто сошлюсь на owasp.org, занятое чтиво — www.owasp.org/index.php/HTML5_Security_Cheat_Sheet, хоть и неполное (в добавку про service workers — sirdarckcat.blogspot.ru/2015/05/service-workers-secure-open-redirect.html, sirdarckcat.blogspot.ru/2015/05/service-workers-new-apis-new-vulns-fun.html)Content Security Policy В целом то браузер он такой — доверчивый, ему откуда сказали данные загрузить — CSS/JS/картинки, он от туда и загрузил. Полезно и для атакующего, просто подключать свой злобный js файл с другого домена при XSS атаке. Чтобы снизить риски от XSS атак (и не только) придумали Content Security Policy (CSP).Этот заголовок сообщает браузеру, откуда можно грузить ресурсы (например — js), а откуда нельзя. Соответственно злоумышленник даже имея возможность внедрить js выполнить его не сможет (например, если запрещены inline js + внешние только с доверенных доменов).
Ошибка подгрузки JS файла с домена, которого нет в «белом» списке
Но бывает так, что злоумышленник все-таки может загрузить свой js и выполнить его, например если приложения (к письму, сообщению) находятся на домене из белого списка.
Более подробно CSP разбирался много раз, в том числе на последней встрече OWASP Russia в Москве, презентация — www.slideshare.net/OWASP-Russia/yourprezi-49135416
Я это серьезно? Про flash? Вообще — да. Для кого-то это актуально, а к многих он исторически где-то еще работает. Да уже в этом году был один масштабный случай с флешем с «ново-старой» багой, про который в рунете так никто не написал :(Но давайте по порядку.crossdomain.xml
Самое популярное связанное с флэшем — это файл crossdomain.xml. Это xml файл, который указывает флэш приложению политику работы с данным доменом (снова речь про SOP). И здесь ситуация как с CORS не пройдет. Если указано, что любой домен имеет доступ (вместе с куками) — значит любой. И это, конечно, плохо. Пример такого файла:
XSS через Flash XSS возможен и через Flash файлы. Пример уязвимого AS кода getURL (_root.URI,'_targetFrame'); Пример эксплуатации: http://victim/file.swf? URI=javascript: evilcode Флэш попытается перейти по адресу javascript: и выполнить произвольный JS код. SWF легко декомпилится, поэтому исходники (напрямую) не нужны. Почитать более подробно про этот способ атаки и найти список уявизмых функция можно на OWASP — www.owasp.org/index.php/Testing_for_Cross_site_flashing_(OTG-CLIENT-008)CVE-2011–2461 IS BACK! Ну, а теперь из свеженького про флэш из 2015. Этой весной на Troopers был представлен рабочий PoC и тулзы для анализа, как проэксплуатировать одну старую уязвимость во флэш файлах, собранных на Adobe Flex (уязвимой версии). Это очень популярный фреймворк для флэш разработчиков. Вектор такой — находим на ресурсе такую флэшку (собранную с уязвимым Adobe Flex), генерим для нее специальный пэйлоад (ресурс) и подключаем. Исполняется флэшка с загружаемого домена с нашей полезной нагрузкой. Итог — снова обход SOP.Для проверки таких файлов (уязвим SWF или нет) можно использовать тулзу — github.com/ikkisoft/ParrotNG, запускается очень просто
java -jar parrotng_v0.2.jar
Вроде об этом 1000 и 1 раз сказали, но снова повторим. Каждый ресурс без доп. настроек можно показать во фрейме. Фрейм грузится с куками текущего пользователя, т.е. во фрейме он открывает ресурс авторизованным (если авторизовался, конечно же). Данный фрейм встраивается атакующим на свою страницу, поверх рисуется что либо призывающее к действию (нажать на кнопку и т.п.), юзер кликает, но по факту он кликает на элементы во фрейме (например — подписывается на группы). Атака называется Clickjacking.X-Frame-Options в ответе веб-сервера позволяет
Полностью запретить показ страницы во фрейме Частично запретить (например, разрешить только для того же origin)
Статья про clickJacking в ВКонтакте
Для расширения возможностей браузера есть плагины (например — Flash), а есть расширения. Вторые пишутся с использованием как раз HTML/CSS/JS, о чем и идет речь в данной статье. Приложения для SmartTV пишутся по схожему способу, используя расширенное API телевизоров (например — доступ к камере). И естественно и тут можно накосячить. Про безопасность расширений я уже рассказывал, а безопасность SmarTV приложений (и векторы атак на пользователей SmartTV) мы обсудим позже. Оставлю только видео[embedded content]
Ну, а если для кого-то из читателей фронтенд это — «Я ПРОСТО ПИШУ CSS И ВСТАВЛЯЮ КАРТИНКИ! РАССКАЖИ МНЕ ПРО БЕЗОПАСНОСТЬ!»… то и вот история.При переходе (в «типичных» условиях) по ссылке