Noveo

Наш блог XSS- и CSRF-атаки и способы борьбы с ними

XSS- и CSRF-атаки и способы борьбы с ними

Фронтенд-разработчик Noveo Георгий рассказывает об основных уязвимостях frontend-приложений XSS и CSRF и разбирает дефолтную политику браузера, возможные пути ее обхода или более точной настройки в случае необходимости.

Same Origin Policy

В контексте веб-безопасности есть два основных вектора атаки, которые необходимо учитывать: XSS и CSRF. Для работы с ними необходимо понимать дефолтную политику браузера насчет взаимодействия между вкладками (точнее, взаимодействия между скриптами с разным origin’ами). Эта политика называется Same Origin Policy (SOP), центральным понятием которой является Origin (~ источник) скрипта/документа. Origin какого-либо скрипта определяется комбинацией трех понятий: протокола (http/https, etc.), хоста (my-website.com) и порта (например, 443 для https и 80 для http). Вот примеры, которые браузер будет интерпретировать как скрипты с одинаковым origin’ом (если на открытой вкладке присутствует 3 скрипта, origin’ы которых отличаются подобным образом, то браузер будет воспринимать их как скрипты с одним origin’ом):

https://my-website.com/home

https://my-website.com/settings

https://my-website.com/profile

 

А такие origin’ы будут считаться разными:

http://my-website.com/home

https://melicious-website/home

https://my-website.com:444

 

Важной особенностью SOP является то, что скрипт с одним origin’ом может посылать запросы на другой origin, однако не сможет прочитать ответ на него (чтение ответа будет заблокировано на уровне браузера). Если требуется заблокировать в том числе отправку запросов (скажем, вы не хотите, чтобы скрипты, загруженные на вашем сайте с других origin’ов, могли делать запрос на origin сайта), это тоже возможно (и более того, рекомендуется) реализовать посредством указания Content Security Policy, но дефолтная политика (SOP) работает именно так.

Noveo SOP Confusion

XSS / JS-Injections

Наиболее часто встречающейся атакой является Cross-Site Scripting («Межсайтовый скриптинг»), или более удачное, на мой взгляд, название — JS-инъекции (по аналогии с SQL-инъекциями).  Если в html-документ вставить строку, в которой будут использоваться элементы, похожие на html-теги, то браузер будет интерпретировать их как валидный html-код и исполнять его. Например, можно прописать <img> с пустым src и добавить onerror=“alert(‘hacked’)” (так как src пустой, то браузер сразу перейдет к вызову onerror), внутри которого выполнить любой JS-код (имея доступ к localStorage и т.п).  Также можно поместить вредоносный скрипт внутрь запроса query params в следующем виде:

Noveo XSS / JS-Injections

Предотвращение JS-инъекций

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

Noveo Предотвращение JS-инъекций

В Angular’е можно за’inject’ить санитайзер и выбрать тот уровень санитизации, который подходит под ситуацию (например, можно разрешить чтение стилей, но при этом экранировать любые скрипты и тп).

Noveo DomSanitizer Angular

Выглядит это так: this.safeString = this.domSanitizer.bypassSecurityTrustHtml(dangerousString);

 

Помимо санитизации существует Content Security Policy (CSP) Header. Через него можно явно задать, скрипты с каким origin’ом могут исполняться браузером на текущей странице. По дефолту Content Security Policy содержит значение * , то есть браузеру разрешено запускать скрипты с любым origin’ом (включая inline-скрипты без origin’а). Такие настройки считаются очень небезопасными, и рекомендуется всегда явно указывать, скрипты с каким origin’ом могут быть запущены на сайте (явное указание ограниченного набора origin’ов заодно предотвращает исполнение inline-скриптов. Чтобы разрешить их исполнение, требуется явно это прописывать).

 

Пример того, как может выглядеть CSP Header:

Content-Security-Policy:
default-src 'self';
img-src *;
media-src media1.com media2.com;
script-src userscripts.example.com

Такие настройки означают, что изображения могут подгружаться с любого источника (*), media только с двух источников (media1.com & media2.com) и скриптам позволено запускаться, если они имеют только один origin (userscripts.example.com). default-src ‘self’  позволяет запускать скрипты с текущего origin’а, но не его поддоменов.

 

Посредством CSP-заголовка можно очень тонко настраивать исполнение скриптов на сайте.

 

content-security-policy Header и access-control-allow-origin немного похожи тем, что CSP позволяет обходить SOP (дефолтную политику браузера насчет запуска скриптов в зависимости от их origin’а), в то время как access-control-allow-origin позволяет получать разрешение на CORS (отвечать (response) на запросы скриптов с другим origin’ом). В начале статьи я упоминал, что SOP позволяет скриптам с одним origin’ом делать запросы на другой origin, однако SOP заблокирует ответ. Посредством access-control-allow-origin можно получить доступ к этому ответу (если на сервере явно прописан origin, который имеет право получать ответы).

 

Если кратко и утрированно, то SCP — это про SOP и frontend-часть системы, а access-control-allow-otigin — это про CORS и backend-часть системы.

CSRF (XSRF)

Noveo CSRF (XSRF)

Второй по распространенности тип уязвимости — это Cross-Site Request Forgery (Межсетевая подделка запроса). Этот вектор атаки использует склонность браузера неявно добавлять куки к любому запросу, независимо от того, с какой вкладки он происходит. Например, вы авторизовались на сайте (предположим, банк), на котором для этого используются куки, и в какой-то момент некий скрипт делает запрос на origin этого банка (вы открыли почтовый клиент и перешли по вредоносной ссылке). В этом случае браузер автоматически добавит куки к запросу, даже если сам запрос происходит с другой вкладки. И да, из-за SOP этот скрипт не будет иметь доступа к response, но он сможет выполнять запросы на правах авторизованного пользователя, например, сделать запрос на перевод денег или просто на удаление любой возможной информации (некоторые атаки происходят не ради конкретной выгоды, а просто потому, что хакер может их произвести).

 

Для предотвращения этой атаки в первую очередь рекомендуется использовать явные системы аутентификации (session token / jwt в header или session token в body param). Также, если на сайте используется какая-то форма, то рекомендуется реализовывать anti-csrf token’ы, суть которых в том, что сервер в response отдает два токена (случайных), один предназначен для куков, другой прячется в скрытом поле формы. Когда клиент submit’ит форму, то он должен отправить оба токена обратно на сервер, и в случае, если они отличаются от того, что сервер выпустил изначально, сервер блокирует запрос.

Integrity

Noveo CIA triad

В контексте информационной безопасности существует понятие CIA triad, объединяющее три аспекта: конфиденциальность (доступ к информации должен иметь только тот, кому она предназначается), доступность (информация должна быть доступна в нужном объеме и в нужное время) и связность. Последнее подразумевает то, насколько части системы могут доверять передаваемым друг другу сообщениям (не произошло ли в процессе передачи нарушения данных). В web’е это можно представить в виде отношений между frontend’ом и backend’ом, и в html существует специальный атрибут integrity, позволяющий запускать скрипт только в том случае, если он не отличается от того, что изначально подразумевали его разработчики.

 

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

 

<script src="https://example.com/example-framework.js"
integrity=“sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC”>

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

 

 В случае Angular’а включить генерацию таких хешей можно с помощью флага —subresource-integrity.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

НазадПредыдущий пост ВпередСледующий пост

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: