Дизайн API Rails без отключения защиты CSRF

Еще в феврале 2011 года Rails был изменен на требовать токен CSRF для всех не-GET, даже для конечной точки API, Я понимаю объяснение того, почему это важное изменение для запросов браузера, но этот пост в блоге не дает никаких рекомендаций относительно того, как API должен обрабатывать изменения.

Мне не интересно отключать защиту CSRF для определенных действий.

Как API должен иметь дело с этим изменением? Ожидается ли, что клиент API сделает GET-запрос API для получения токена CSRF, а затем включает этот токен в каждом запросе во время этого сеанса?

Похоже, что токен не меняется с одного POST на другой. Можно ли предположить, что токен не будет меняться в течение всего сеанса?

Я не наслаждаюсь дополнительной обработкой ошибок, когда истекает срок действия сеанса, но я полагаю, что это лучше, чем иметь GET токен перед каждым запросом POST/PUT/DELETE.

Ответ 1

Старый вопрос, но безопасность важна настолько, что я считаю, что он заслуживает полного ответа. Как обсуждалось в этом question, по-прежнему существует некоторый риск CSRF даже с API. Предполагается, что браузеры предостерегают от этого по умолчанию, но поскольку у вас нет полного контроля над браузером и плагинами, которые пользователь установил, все равно следует считать лучшей практикой защиты от CSRF в вашем API.

То, как я иногда это делал, - это проанализировать метатег CSRF на самой HTML-странице. Мне это не очень нравится, поскольку он не очень хорошо сочетается с тем, как работает много приложений с одной страницей + API, и я чувствую, что токен CSRF должен быть отправлен в каждом запросе независимо от того, является ли он HTML, JSON или XML.

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

Вот как я это сделал с AngularJS:

  # In my ApplicationController
  after_filter :set_csrf_cookie

  def set_csrf_cookie
    if protect_against_forgery?
      cookies['XSRF-TOKEN'] = form_authenticity_token
    end
  end

AngularJS автоматически ищет файл cookie с именем XSRF-TOKEN, но не стесняйтесь называть его тем, что вы хотите для своих целей. Затем, когда вы отправляете POST/PUT/DELETE, вы должны установить свойство заголовка X-CSRF-Token, которое автоматически ищет Rails.

К сожалению, AngualrJS уже отправляет файл XSRF-TOKEN cookie в значение заголовка X-XSRF-TOKEN. Легко переопределить поведение Rails по умолчанию, чтобы разместить это в ApplicationController следующим образом:

  protected

  def verified_request?
    super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
  end

Для Rails 4.2 теперь имеется встроенный помощник для проверки CSRF, который должен использоваться.

  protected

  def verified_request?
    super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
  end

Надеюсь, что это полезно.

EDIT: в обсуждение этого вопроса для запроса запроса Rails. Я представил, что передача токена CSRF через API для логин - особенно плохая практика (например, кто-то может создать сторонний логин для вашего сайта, который использует учетные данные пользователя вместо токенов). Итак, кабальный эмиттер. Вам решать, насколько вы обеспокоены тем, что касается вашего приложения. В этом случае вы все равно можете использовать вышеупомянутый подход, но только отсылаете CSRF файл cookie в браузер, который уже имеет аутентифицированный сеанс, а не для каждого запроса. Это предотвратит отправку действительного входа без использования метатега CSRF.

Ответ 2

Rails работает со стандартом "безопасный по умолчанию". Кросс-сайт или кросс-сессия запроса Forgery требует, чтобы пользователь имел браузер и другой доверенный веб-сайт. Это не относится к API, поскольку они не запускаются в браузере и не поддерживают сеанс. Следовательно, вы должны отключить CSRF для API.

Конечно, вы должны защищать свой API, требуя HTTP-аутентификацию или пользовательский реализованный токен API или решение OAuth.