Cross-site request forgery — это атака с целью выполнения скрытых от жертвы вредоносных запросов к доверенному сайту, на котором жертва совершила вход в свой аккаунт. Атака возможна из-за того, что ассоциированные с ресурсом cookie-файлы автоматически используются для запросов к нему. Браузер не различает, был ли сделан запрос именно с этого сайта или со стороннего. Следовательно, злоумышленник может совершить атаку, не имея доступа ко входным данным пользователя или cookie.
Как правило, приложение уязвимо к CSRF в случае, если взаимодействие клиентской и серверной частей построено небезопасным образом. Например, взаимодействие клиента и сервера происходит через GET-запросы. Запросы производятся через обращения к URL с параметрами. Этот URL может выглядеть так:
domainname/DoAction?ActionName=DeleteReport&ReportName=ThatReport
Браузер выполняет GET-запрос к серверу, передавая ему название метода и его аргументы. Этот пример предполагает удаление какого-то отчёта ('ActionName=DeleteReport'). Это уже выглядит странно: по идее, запросы получения не должны ничего удалять.
Название отчёта передаётся в параметре 'ReportName'. Злоумышленник, имея информацию о названиях отчётов, может подставить любое подходящее значение.
Выполнение запроса по этому URL произведёт удаление отчёта. При этом злоумышленнику даже не требуется доступ к компьютеру или аккаунту жертвы, хотя удаление будет произведено от её имени.
Для совершения атаки злоумышленник формирует такой URL, при обращении к которому производятся вредоносные действия.
Атака может быть произведена через HTML-разметку. Это означает, что атака может быть проведена неявно при обработке страницы. Например, в атрибут 'src' (источник) тега 'img' (изображение) можно поместить любой URL. Браузер при загрузке страницы отправит запрос по этому URL, пытаясь получить изображение.
Итак, злоумышленник создаёт свою страницу с элементом 'img' нулевого размера и использует в качестве URL изображения свой сконструированный запрос:
<img
src="domainname/DoAction?ActionName=DeleteReport&ReportName=ThatReport"
width="0"
height="0">
Дальше он применяет социальную инженерию — побуждает жертву загрузить свою вредоносную разметку — открыть страницу или письмо, свёрстанное на HTML. Запрос будет выполнен неявно, и жертва не заметит вредоносных действий.
Этот способ совершения CSRF можно назвать GET-сценарием. Он возможен, если программисты сайта нарушили стандарт, не следуя указаниям об идемпотентности GET-запросов. Не следует использовать GET-запросы для действий, изменяющих состояние.
Кроме того, существуют способы атаки с использованием других HTTP-методов. Например, создание страницы с JavaScript-кодом, совершающим запрос с любым HTTP-методом, будь то GET, POST, PUT и другие. Для этого можно использовать fetch API или объект 'XMLHttpRequest'. Вредоносный код может быть выполнен неявно для жертвы, например, на обработке события жизненного цикла страницы 'onload'. Ниже представлен пример кода, производящего запрос:
<script>
function sendRequest(){
var req = new XMLHttpRequest();
var url = 'domainname/DoAction';
var args = 'ActionName=DeleteReport&ReportName=ThatReport';
req.open('POST', url, false);
req.('Content-type', 'application/x-www-form-urlencoded');
req.send(args);
}
window.onload = sendRequest;
</script>
Злоумышленник, используя социальную инженерию, побуждает жертву открыть вредоносную страницу. Последствия открытия страницы сходны с таковыми для GET-сценария, при этом запрос к уязвимому серверу будет неявным и жертва не заметит его отправки.
Также существует POST-сценарий CSRF с использованием HTML-разметки. Он может быть использован, если в браузере жертвы отключено выполнение JavaScript. Злоумышленнику также не требуется доступ к компьютеру или аккаунту жертвы. К примеру, атакуемый сайт предполагает взаимодействие клиентской и серверной частей через отправку данных, производя POST-запросы с помощью HTML-тега 'form'. Этот тег позволяет указать URL обработчика запроса и используемый HTTP-метод. Сервер ожидает получения POST-запроса с полезной нагрузкой — названием действия и параметрами для этого действия. Например, URL обработчика, определяемый в атрибуте 'action', выглядит так:
domainname/DoAction
Значение атрибута 'method' отвечает за HTTP-метод. Тогда разметка может иметь такой вид:
<form action="domainname/DoAction" method="post">
<input type="hidden" name="ActionName" value="DeleteReport"/>
<input type="hidden" name="ReportName" value="ThatReport"/>
<input type="submit" value="Some clickbaiting text"/>
</form>
Как и в предыдущем случае, имея информацию о названиях действий и отчётов, злоумышленник может подставить любые подходящие значения. Эти значения, из которых будет сформирована полезная нагрузка запроса, хранятся в скрытых полях ввода.
Когда жертва нажмёт на кнопку отправки формы, будет сформирован POST-запрос к URL с полезной нагрузкой из вышеописанных скрытых полей.
Как и в предыдущем сценарии, злоумышленник применяет социальную инженерию, чтобы побудить жертву перейти на страницу. Но помимо открытия вредоносной страницы, сценарий требует явных действий на ней — нажатия кнопки отправки формы.
Эти примеры иллюстрируют, что корректно реализованные API также уязвимы. Более того, отключение выполнения JavaScript не помогает побороть проблему.
Как уже сказано выше, не следует использовать GET-запросы для действий, изменяющих состояние. Кроме GET и POST стандарт HTTP описывает множество методов, предназначенных для разных целей: PUT, DELETE и т. д. Например, для выполнения действий удаления следует использовать HTTP-метод 'DELETE'. Таким образом, первым шагом борьбы с CSRF будет корректная реализация API. Как вариант, это может быть REST API.
В таком случае корректная реализация API не должна смешивать получение ресурсов с выполнением действий, а используемые HTTP-методы должны иметь семантику, соответствующую описанной в стандарте. Впрочем, корректная реализация сама по себе не защищает от CSRF. Как было описано выше, используя JavaScript, можно произвести любой запрос к любой конечной точке API. Требуются дополнительные проверки безопасности — идентификация среды, в которой был произведён запрос.
Статья с сайта OWASP предлагает несколько паттернов решения проблемы CSRF. Их объединяет требование наличия верифицирующего токена. Когда пользователь переходит к уязвимому элементу, например, к форме для заполнения, сервер возвращает вместе с контентом верифицирующий токен. Количество токенов и место их нахождения (в разметке, в cookie и т. д.) зависит от используемого паттерна защиты. Выбор паттерна зависит от того, как загружается уязвимый элемент (статическая страница, AJAX и т. д.) и характеристик сервера (stateful или stateless). Когда пользователь выполняет действия, которые вызывают отправку данных на сервер, верифицирующий токен отправляется вместе с ними. Если во входящих данных запроса токен отсутствует или он некорректен, то сервер не будет обрабатывать такой запрос.
0