Иван Гришаев в своей статье Руководство по кросс-доменным запросам (CORS) подробно рассказываем про механизм осуществления запроса на другой сервер. В частности, он описывает «простые» и «сложные» запросы.
Простым считается запрос методами:
- HEAD
- GET
- POST
и заголовками:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type, но только со значениями:
— application/x-www-form-urlencoded
— multipart/form-data
— text/plainЕсли ваш запрос удовлетворяет этим критериям, можно слать Аякс к другому домену из любого современного браузера.
Вот такие простые запросы злоумышленники могут эксплуатировать для CSRF-атак.
fetch('https://jsfiddle.net/echo/html/', {
method: 'POST',
credentials: 'include',
body: new URLSearchParams({
html: 'My test response'
})
});
Даже если ответ не был принят браузером, тем не менее сервер уже совершил какие-то операции. А так как вместе с запросом автоматически передаются cookie, то эти операции могу быть выполнены от лица авторизованного пользователя или администратора.
Как защититься?
Принимаем только запросы с заголовком
Content-Type: application/json
.Это автоматически делаем запрос «сложным». Он будет выполнен в 2 этапа: сначала браузер проверит какие заголовки разрешены для CORS-запроса, а затем выполнит основной запрос.
Правильно выставляем заголовки
Access-Control-Allow-Origin
,Access-Control-Allow-Headers
,Access-Control-Allow-Methods
иAccess-Control-Max-Age
.Авторизацию пользователя выполняем только по значению в заголовке, а не по cookie.
Стоит отметить, что хранить авторизационный токен в cookie не опасно. Но использовать его на сервере нельзя так как все cookie для домена, на который выполняется запрос, как я уже упомянул, пересылаются автоматически.
Заголовки же можно выставить только явно при формировании запроса. Но злоумышленник не будет иметь доступа к нужным данным в браузере и, соответственно, физически не сможет совершить запрос от лица авторизованного пользователя.