Cross-Site Request Forgery [CSRF]

1. Введение:

Подделка межсайтовых запросов (также известная как CSRF) - это уязвимость веб-безопасности, позволяющая злоумышленнику побудить пользователя выполнить действия, которые он не собирался выполнять. Она позволяет злоумышленнику частично обойти политику same-origin, которая призвана предотвратить вмешательство различных веб-сайтов друг в друга. В обычном сценарии атаки GET запрос, изменяющий состояние эксплуатируемого веб-сервера.

2. Методы защиты:

2.1. Следуя архитектуре REST:

REST или Representational State Transfer утверждает, что GET должны использоваться только для получения данных или других ресурсов, а для любых других действий, которые могут существенно изменить состояние сервера, следует использовать один из соответствующих протоколов, например PUT , POST и DELETE .

Поскольку не все действия имеют очевидный соответствующий HTTP-метод, например, получение = GET , обновление = POST , создание = PUT , удаление = DELETE Существуют и другие меры, которые могут максимально обезопасить наше приложение, но пока важно помнить, что GET должен использоваться только для получения данных.

2.2. Работа с токенами CSRF:

Это один из наиболее рекомендуемых методов правильной борьбы с CSRF.

Токены CSRF предотвращают CSRF, поскольку без них злоумышленник не сможет создать ни одного корректного запроса к внутреннему серверу.

Ниже перечислены несколько техник и вариантов использования этих токенов.

2.2.1. Токен синхронизатора:

В идеале разработчик должен хранить CSRF-токены на стороне сервера. В зависимости от реализации, это могут быть токены для каждой сессии или для каждого запроса.

Токены на запрос обычно более безопасны, чем токены на сессию, так как период их действия довольно ограничен; однако это накладывает ограничения на удобство использования, например, использование кнопки "назад" в браузере не сработает, так как сессия уже истекла.

При выдаче запроса клиентом серверный компонент должен был проверить наличие и действительность токена в запросе по сравнению с токеном в сессии. Если токен либо отсутствует вовсе, либо токен запроса не соответствует значению в сессии, запрос должен быть прерван и даже помечен как потенциальная CSRF-атака.

При разработке CSRF-токенов разработчик должен знать, что токены должны:

  • Быть уникальными для каждой сессии пользователя.

  • Иметь секрет

  • Быть трудно угадываемыми (обычно создается с помощью безопасного метода генерации).

Токены CSRF НЕ должны передаваться через cookies.

Токены CSRF можно добавлять через скрытые поля, заголовки, использовать в формах и вызовах AJAX. Важно проверять любые возможные утечки, например, в логах сервера или даже в URL. Здесь приведен пример применения токена к форме.

<form action="/pay" method="post">
    <input type="hidden" name="CSRFtoken" value="ZjJkMzA3YWEyMzg2YTBjNzQzM2NlODUwMTllZTU2MTk=">
    [...]
</form>

Поддерживать состояние для CSRF-токенов иногда проблематично, поэтому альтернативой для синхронизатора токенов является техника двойного отправления cookie.

Когда пользователь заходит на сайт (желательно до аутентификации), веб-сайт должен (безопасно сгенерировать) значение и установить его в качестве cookie в браузере пользователя. Таким образом, приложение требует, чтобы любой запрос действия включал это значение в качестве скрытого значения формы. Таким образом, если значение скрытой формы и cookie совпадают на стороне сервера, сервер принимает запрос как легитимный, в противном случае он отклоняет его.

Обратите внимание, что этот метод является в некотором роде обходным путем, и желательно использовать его вместе с каким-либо способом шифрования этих файлов cookie или работать с HMAC.

2.3. Использование пользовательских заголовков запросов:

Добавление CSRF-токенов, использование двойного cookie submit или другие способы защиты, связанные с изменением пользовательского интерфейса, часто могут быть сложными или иными проблемами. Альтернативный метод защиты, который особенно хорошо подходит для конечных точек AJAX или API, - это использование пользовательского заголовка запроса. Он основан на политике однородности (same-origin policy, SOP), которая гласит, что JavaScript может использоваться для добавления пользовательского заголовка, ограниченного строго рамками его происхождения. Однако по умолчанию браузеры не позволяют JavaScript делать какие-либо кросс-оригинальные запросы с пользовательскими заголовками.

Поскольку POST , PUT , PATCH и DELETE являются методами, изменяющими состояние, они должны использовать CSRF-токен, прикрепленный к запросу. Следующее руководство продемонстрирует, как создать переопределения в библиотеках JavaScript, чтобы маркеры CSRF автоматически включались в каждый AJAX-запрос для методов, изменяющих состояние, упомянутых выше.

<script type="text/javascript">
    var csrftoken = document.querySelector("meta[name='csrf-token']").getAttribute("content");

    axios.defaults.headers.post['anti-csrf-token'] = csrftoken;
    axios.defaults.headers.put['anti-csrf-token'] = csrftoken;
    axios.defaults.headers.delete['anti-csrf-token'] = csrftoken;
    axios.defaults.headers.patch['anti-csrf-token'] = csrftoken;

</script>

2.4. Использование промежуточного ПО для защиты от CSRF:

В конце концов, использование "проверенного в боях" метода, такого как модуль, является одним из самых надежных средств защиты. В качестве доказательства концепции я покажу, как использование csurf поможет вам генерировать и управлять CSRF-токенами.

// server.js
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
 
// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
 
// create express app
var app = express()
 
// parse cookies
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
 
app.get('/form', csrfProtection, function (req, res) {
  // pass the csrfToken to the view
  res.render('send', { csrfToken: req.csrfToken() })
})
 
app.post('/process', parseForm, csrfProtection, function (req, res) {
  res.send('data is being processed')
})

А теперь установите csrfToken в качестве значения скрытого поля ввода под названием _csrf.

<form action="/process" method="POST">
  <input type="hidden" name="_csrf" value="{{csrfToken}}">
  
  Favorite color: <input type="text" name="favoriteColor">
  <button type="submit">Submit</button>
</form>

Last updated