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

Відповіді:


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

4
це говорить про те, що всі API обслуговують трафік від програми до програми (в якій одному партнеру видається унікальний ключ та секрет). У такому випадку, як і зв’язок між серверами, така відповідь є доречною. Втім, більшість розробників веб-додатків бентежить те, що для клієнта Javascript, керованого та написаного вами, ви НЕ ХОЧЕТЕ використовувати єдиний секретний ключ (який би відкрив єдиний секретний ключ для всіх клієнтів). Натомість механізм CSRF та файлів cookie Rail працює чудово - навіть для програм Javascript, які використовують ваш API - якщо ви передаєте маркер CSRF назад Rails з кожним запитом.
Jason FB

спосіб, як ви це робите в AJAX, описаний у цій публікації SO stackoverflow.com/questions/7203304/…
Джейсон ФБ

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

Якщо ви надаєте або будуєте альтернативу CSRF, будьте моїм гостем. Просто знайте причину того, що ви замінюєте, щоб ви знали, що доцільно замінити. Очевидно, що тепер наш прискорення стає контекстуальним.
Джейсон ФБ

77

Інший спосіб вимкнути CSRF, який не відображає нульовий сеанс, - це додати:

skip_before_action :verify_authenticity_token

у вашому Rails Controller. Це забезпечить вам доступ до інформації про сеанс.

Знову ж таки, переконайтеся, що ви робите це лише в контролерах API або в інших місцях, де захист CSRF не зовсім застосовується.


1
Це те саме, що protect_from_forgery except: [:my_method_name]?
Арнольд Роа

19

На api.rubyonrails.org є відповідна інформація про конфігурацію CSRF щодо контролерів API :

Важливо пам’ятати, що це також впливає на запити 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. Без secure_from_forgery, не вимикаючи його та не встановлюючи на ньому крім, ви просите Rails НЕ захищати цю дію, використовуючи інформацію з маркера CSRF (який береться із файлу cookie сеансу).
Jason FB

5
Я думаю, що бентежить те, що для документації Rails "API" означає додаток від сервера до сервера, який буде отримувати запити API від надійних віддалених партнерів (не від усіх веб-користувачів, які відвідують ваш сайт). Для тих, хто надіслав особу, дайте унікальні пари секрет ключ-ключ за допомогою безпечного, не зламаного механізму. Для сучасних веб-додатків, де багато людей завантажують вашу веб-програму через веб-клієнт, ви все одно використовуєте механізм на основі сеансу або маркера, щоб однозначно ідентифікувати кожну людину, яка відвідує ваш сайт. Тож якщо ви не використовуєте ДЛЯ ДРУГОГО механізму для цього (веб-маркери Json тощо), дотримуйтесь вбудованих матеріалів Rails.
Jason FB

1
спосіб, як ви це робите в AJAX, описаний у цій публікації SO stackoverflow.com/questions/7203304/…
Джейсон ФБ

12

З Rails 5 ви також можете створити новий клас за допомогою :: API замість :: Base:

class ApiController < ActionController::API
end

3

Якщо ви хочете виключити вибіркову дію контролера вибірки

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.