rails - «ВНИМАНИЕ: не удается проверить подлинность токена CSRF» для запросов на разработку json.

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 вместе с данными публикации, которые вы затем проверяете перед выполнением желаемого метода.
Райан Крюс
18
В rails 4 вы можете сделать что-то вроде:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
genkilabs
2
@genkilabs Мне это нравится, добавлено в ответ для пользователей rails 4, спасибо =]
Ryan Crews
1
Я только что сделал две вещи, чтобы заставить эту работу работать в моем приложении Rails и iOS: 1) Я не видел причин, по которым мы смешивали хэш-синтаксис старого и нового стиля в Ruby, поэтому код в моем контроллере выглядит так: protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2) Не имеет отношения к этому вопросу, но относится к теме POSTing JSON из iOS, поскольку я использую, AFNetworkingа Rails не обертывает параметры автоматически, я сделал следующее:manager.requestSerializer = [AFJSONRequestSerializer serializer];
mharper
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, просто добавьте к postданным действительное поле authentication_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

Гусябар
источник
Я не уверен, что вы захотите это сделать - браузеры могут использовать JSON API.
Джо Ван Дайк
Спасибо! Нужен был быстрый (и грязный) способ решить эту проблему, и он может работать достаточно хорошо для того, чего я пытаюсь достичь.
NerdyTherapist
Связанный документ (теперь обновленный для Rails 5) выглядит так: protect_from_forgery with: :exception, unless: -> { request.format.json? }и у меня это сработало. Благодарю.
akostadinov
1
Это просто отключает проверку, что позволяет избежать проблемы. Если и приложение собирается отключить это, то какой смысл включать защиту от подделки?
jefflunt
3

Что беспокоит, так это то, что в Rails 3.2.3 мы теперь получаем предупреждение CSRF в production.log, но сообщение не терпит неудачу! Я хочу, чтобы он провалился, потому что он защищает меня от нападок. И вы можете добавить токен csrf с помощью jquery перед фильтром btw:

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' }
DaCart
источник
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 / sessions / create.js.rb.

Теперь у него есть действующий csrf-токен :) Надеюсь, это поможет

nbit001
источник
0

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

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

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

protect_from_forgery
Merqlove
источник