Rails: не удается проверить подлинность токена CSRF при выполнении запроса POST


89

Я хочу передать POST requestсвоему местному разработчику вот так:

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

Однако с консоли сервера он сообщает

Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
  ActiveRecord::SchemaMigration Load (0.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
  Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

Вот мой контроллер и настройка маршрутов, это довольно просто.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

Я не уверен, что мне нужно делать? Отключение CSRF, безусловно, сработает, но я думаю, что это должно быть моей ошибкой при создании такого API.

Есть ли какие-то другие настройки, которые мне нужно сделать?


5
Для API обычно принято отключать проверку токена CSRF . Пользуюсь protect_from_forgery with: :null_session.
— dcestari 03

Ответы:


110

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

Защита Rails CSRF сделано для «классических» веб - приложений - это просто дает степень уверенности в том , что запрос возник из вашего собственного веб - приложения. Токен CSRF работает как секрет, который знает только ваш сервер - Rails генерирует случайный токен и сохраняет его в сеансе. Ваши формы отправляют токен через скрытый ввод, и Rails проверяет, что любой запрос, отличный от GET, включает токен, который соответствует тому, что хранится в сеансе.

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

Вместо этого вам следует использовать стратегию на основе токенов аутентификации запросов API с помощью ключа и секрета API, поскольку вы проверяете, что запрос исходит от утвержденного клиента API, а не из вашего собственного приложения.

Вы можете отключить CSRF, как указано @dcestari:

class ApiController < ActionController::Base
  protect_from_forgery with: :null_session
end

Обновлено. В Rails 5 вы можете создавать приложения только с API, используя --apiопцию:

rails new appname --api

Они не включают промежуточное программное обеспечение CSRF и многие другие компоненты, которые являются суперпроизводителями.


Спасибо, я решил отключить часть CSRF: stackoverflow.com/questions/5669322/…
— cqcn1991 04

4
это говорит о том, что все API обслуживают трафик между приложениями (в котором одному партнеру выдается уникальный ключ и секрет). В этом сценарии, как при межсерверном взаимодействии, этот ответ уместен. ОДНАКО большинство разработчиков веб-приложений сбивает с толку то, что для клиента Javascript, управляемого и написанного вами, вы НЕ ХОТИТЕ использовать один ключ-секрет (который предоставит один ключ-секрет всем клиентам). Вместо этого CSRF и механизм сеанса cookie в Rail отлично работают - даже для приложений Javascript, которые используют ваш API, - если вы передаете токен CSRF обратно в Rails с каждым запросом.
— Джейсон ФБ

способ, которым вы делаете это в AJAX, описан в этом сообщении SO stackoverflow.com/questions/7203304/…
— Джейсон FB

@JasonFB, вы правы в том, что один секрет с клиентами javascript не работает. Однако использование сессий и защиты Rails CSRF по-прежнему проблематично, если вы собираетесь создать API, который также может использоваться другими типами клиентов, не являющихся браузерами.
— максимум

Если вы предоставите или создадите альтернативу CSRF, будь моим гостем. Просто знайте причину того, что вы заменяете, чтобы знать, чем его заменить. Очевидно, теперь наша придирка стала контекстной.
— Jason FB

77

Другой способ отключить CSRF, который не будет отображать нулевой сеанс, - это добавить:

skip_before_action :verify_authenticity_token

в вашем контроллере Rails. Это гарантирует, что у вас по-прежнему будет доступ к информации о сеансе.

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


1
Это же чем protect_from_forgery except: [:my_method_name]?
— Арнольд Роа

19

Соответствующая информация о конфигурации CSRF применительно к контроллерам API есть на api.rubyonrails.org :

⋮

Важно помнить, что запросы XML или JSON также подвержены влиянию, и если вы создаете API, вам следует изменить метод защиты от подделки в ApplicationController(по умолчанию:) :exception:

class ApplicationController < ActionController::Base
  protect_from_forgery unless: -> { request.format.json? }
end

Возможно, мы захотим отключить защиту CSRF для API, поскольку они, как правило, не имеют состояния. То есть клиент API запроса будет обрабатывать сеанс вместо вас, а не Rails.

⋮


4
Это так сбивает с толку. Я колеблюсь между источниками, которые говорят, что protect_from_forgeryэто все еще необходимо для API, и источниками, которые говорят, что это не так. Что, если API предназначен для одностраничного приложения, которое использует файл cookie сеанса для аутентификации пользователя?
— Уиллиам Джадд

Механизм CSRF - это встроенный в Rails способ борьбы с вектором атаки с использованием файлов cookie сеанса. Этот механизм защищает ваши контроллеры, работая вместе (фактически внутри) с cookie сеанса Rails. Без protect_from_forgery, отключения или установки для него except, вы говорите Rails НЕ защищать это действие, используя информацию из токена CSRF (который берется из файла cookie сеанса).
— Джейсон ФБ

5
Я думаю, что сбивает с толку то, что для документации Rails «API» означает межсерверное приложение, которое будет получать запросы API от доверенных, удаленных партнеров (не всех веб-пользователей, посещающих ваш сайт). Для таких пользователей предоставьте уникальные пары ключ-секрет через безопасный, не поддающийся взлому механизм. Для современных веб-приложений, где множество людей загружают ваше веб-приложение через веб-клиент, вы по-прежнему используете сеанс или механизм на основе токенов, чтобы однозначно идентифицировать каждого человека, посещающего ваш сайт. Поэтому, если вы не используете ДРУГОЙ механизм для этого (веб-токены Json и т. Д.), Придерживайтесь встроенных средств Rails.
— Джейсон ФБ

1
способ, которым вы это делаете в AJAX, описан в этом сообщении SO stackoverflow.com/questions/7203304/…
— Джейсон FB

12

Начиная с Rails 5, вы также можете создать новый класс с :: API вместо :: Base:

class ApiController < ActionController::API
end

3

Если вы хотите исключить действие sample контроллера

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

Вы можете без проблем обрабатывать запросы извне.


0

Самым простым решением проблемы является выполнение стандартных действий в вашем контроллере или вы можете напрямую поместить его в ApplicationController.

class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true end

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.