CSRF-атака на ваш API

Иван Гришаев в своей статье Руководство по кросс-доменным запросам (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, то эти операции могу быть выполнены от лица авторизованного пользователя или администратора.

Как защититься?

  1. Принимаем только запросы с заголовком Content-Type: application/json.

    Это автоматически делаем запрос «сложным». Он будет выполнен в 2 этапа: сначала браузер проверит какие заголовки разрешены для CORS-запроса, а затем выполнит основной запрос.

  2. Правильно выставляем заголовки Access-Control-Allow-Origin, Access-Control-Allow-Headers, Access-Control-Allow-Methods и Access-Control-Max-Age.

  3. Авторизацию пользователя выполняем только по значению в заголовке, а не по cookie.

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

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