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.

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

cqcn1991
источник
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

Ариш Хан
источник