rails - “ПОПЕРЕДЖЕННЯ: Не вдається перевірити справжність токена CSRF” для запитів json devise


82

Як я можу отримати маркер CSRF для передачі із запитом JSON?

Я знаю, що з міркувань безпеки Rails перевіряє маркер CSRF на всіх типах запитів (включаючи JSON / XML).

Я міг би вкласти свій контролер skip_before_filter :verify_authenticity_token, але я втратив би захист CRSF (не доцільно :-)).

Ця подібна (досі не прийнята) відповідь наводить на думку

Отримати маркер за допомогою <%= form_authenticity_token %>

Питання в тому, як? Чи потрібно робити перший дзвінок на будь-яку зі своїх сторінок, щоб отримати маркер, а потім здійснити справжню автентифікацію за допомогою Devise? Або це одноразова інформація, яку я можу отримати зі свого сервера, а потім використовувати послідовно (поки я не зміню її вручну на самому сервері)?

Відповіді:


127

РЕДАГУВАТИ :

У Rails 4 я зараз використовую те, що пропонує @genkilabs у коментарі нижче:

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }

Що замість повного вимкнення вбудованої системи безпеки вбиває будь-який сеанс, який може існувати, коли щось потрапляє на сервер без маркера CSRF.


skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }

Це вимкне перевірку CSRF для повідомлень / путів json, які були належним чином позначені як такі.

Наприклад, у iOS встановіть для вашого NSURLRequest наступне, де "параметри" - це ваші параметри:


[request setHTTPMethod:@"POST"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"content-type"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"accept"];

[request setHTTPBody:[NSData dataWithBytes:[parameters UTF8String] 
                                            length:[parameters length]]];

3
роблячи це, ви піддаєтеся атаці, якщо зловмисник позначає формат як 'json'. І тому Rails за визначенням видає попередження. Я хочу, щоб мій json міг правильно передавати маркер, інакше попередження в журналах - це нормально.

21
Ви піддаєтесь атакам, саме тому вони рекомендують мати інший захист при вимкненні фільтра. Зазвичай для запитів API запитувач надсилає ключ API разом із даними повідомлення, які ви потім перевіряєте перед виконанням бажаного методу.
Ryan Crews

18
У рейках 4 ви можете зробити щось на зразок:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
genkilabs

2
@genkilabs Мені це подобається, додано до відповіді для рейків 4 користувачів, спасибі =]
Райан Крюс

1
Дві речі, які я зараз зробив, щоб зробити це у своєму додатку Rails & iOS: 1) Я не побачив жодної причини, чому ми змішували хеш-синтаксис старого та нового стилю в Ruby, тому код у моєму контролері виглядає так: protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2) Не пов’язане з цим питанням, але пов’язане з темою розміщення JSON з iOS, оскільки я використовую AFNetworkingі Rails не manager.requestSerializer = [AFJSONRequestSerializer serializer];
обертає

18

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

Наприклад, помістіть це у свої сеанси # create:

response.headers['X-CSRF-Token'] = form_authenticity_token

Зразок заголовка відповіді на вхід, що містить маркер CSRF:

HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 35
Content-Type: application/json; charset=utf-8
Date: Mon, 22 Oct 2012 11:39:04 GMT
Etag: "9d719d3b9aabd413c3603e04e8a3933d"
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Set-Cookie: [cut for readability] 
X-Csrf-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=
X-Request-Id: 178746992d7aca928c876818fcdd4c96
X-Runtime: 0.169792
X-Ua-Compatible: IE=Edge

Цей маркер дійсний до повторного входу в систему або (виходу з системи, якщо ви підтримуєте це через свій API). Ваш клієнт може витягти та зберегти маркер із заголовків відповіді на вхід. Потім кожен запит POST / PUT / DELETE повинен встановлювати заголовок X-CSRF-Token із значенням, отриманим під час входу.

Зразок заголовків POST із маркером CSRF:

POST /api/report HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate, compress
Content-Type: application/json; charset=utf-8
Cookie: [cut for readability]
Host: localhost:3000
User-Agent: HTTPie/0.3.0
X-CSRF-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=

Документація: form_authenticity_token


18

Дійсно найпростіший спосіб. Не турбуйтеся зміною заголовків.

Переконайтесь, що у вас є:

<%= csrf_meta_tag %>

у вашому layouts/application.html.erb

Просто зробіть приховане поле введення приблизно так:

<input name="authenticity_token" 
       type="hidden" 
       value="<%= form_authenticity_token %>"/>

Або якщо вам потрібна публікація jquery ajax:

$.ajax({     
    type: 'POST',
    url: "<%= someregistration_path %>",
    data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },                                                                                  
    error: function( xhr ){ 
      alert("ERROR ON SUBMIT");
    },
    success: function( data ){ 
      //data response can contain what we want here...
      console.log("SUCCESS, data="+data);
    }
});

В основному, коли ви публікуєте свої дані json, просто додайте до даних дійсне поле postauthent_token, і попередження повинно зникнути ...


ІМХО йому слід не тільки помістити попередження в реєстратор, але і повністю відкинути повідомлення без дійсного тегу csrf за замовчуванням, він просто розміщує попередження і подає дані чудово ...
Вальтер Шрепперс

Коротший спосіб зробити поле введення прихованого токена автентичності - це = token_tag(nil)(нуль важливий)
Йо Людке,

У старих версіях рейок тег закінчується множиною. <% = csrf_meta_tags%>
cyonder

4

Я вирішив цю помилку таким чином:

class ApplicationController < ActionController::Base
  protect_from_forgery
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected

  def json_request?
    request.format.json?
  end
end

Джерело: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html


Я не впевнений, що ви хотіли б це зробити - браузери можуть використовувати API JSON.
Joe Van Dyk

Дякую! Потрібен швидкий (і брудний) спосіб вирішення цієї проблеми, і це може спрацювати досить добре для того, що я намагаюся досягти.
NerdyTherapist

Зв’язаний документ (тепер оновлений для Rails 5), здається, protect_from_forgery with: :exception, unless: -> { request.format.json? }і працював у мене. Дякую.
Акостадінов

1
Це просто відключає перевірку, що дозволяє уникнути проблеми. Якщо додаток збирається це вимкнути, то який сенс умикати захист від підробки?
jefflunt

3

Що турбує, це те, що в Rails 3.2.3 тепер ми отримуємо попередження CSRF у production.log, але публікація не провалюється! Я хочу, щоб він зазнав невдачі, оскільки захищає мене від атак. І ви можете додати маркер csrf з jquery перед фільтром до речі:

http://jasoncodes.com/posts/rails-csrf-vulnerability


2

Я використав нижче. Використовуючи include? отже, якщо тип вмісту - application / json; charset = utf-8, то він все ще працює.

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format.include? 'application/json' }

2

Ця відповідь є кращою.

Ви можете зберегти перевірку CSRF-TOKEN без особливих зусиль (маркер додається) перед будь-яким надсиланням XMLHttpRequest. Немає JQuery, немає нічого, просто скопіюйте / вставте та оновіть.

Просто додайте цей код.

(function() {
    var send = XMLHttpRequest.prototype.send,
        token = $('meta[name=csrf-token]').attr('content');
    XMLHttpRequest.prototype.send = function(data) {
        this.setRequestHeader('X-CSRF-Token', token);
        return send.apply(this, arguments);
    };
}());

1

У мене була та ж проблема з наступною версією Rails:
gem 'rails',: git => 'git: //github.com/rails/rails.git',: branch => '3-2-stable'

Я оновився до 3.2.2, і зараз у мене все працює нормально. :)
самоцвіт 'рейки', '3.2.2'


дякую за пропозицію. Я спробував, але я все ще маю те саме попередженняWARNING: Can't verify CSRF token authenticity

0

Я натрапив на те саме питання сьогодні ввечері. Причиною цього є те, що коли ви входите в систему, останній маркер csrf вже не дійсний. Що я зробив: $("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>');у вашому додатку / views / devise / session / create.js.rb.

Тепер він має дійсний csrf-маркер :) Я сподіваюся, це допоможе


0

Також для режиму розробки / тестування.

protect_from_forgery with: :exception unless %w(development test).include? Rails.env

Це попередження відображається, оскільки ви використовуєте :null_session, у Rails 4.1 воно працює за замовчуванням, якщо with:параметри не вказані.

protect_from_forgery
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.