rails - „OSTRZEŻENIE: Nie można zweryfikować autentyczności tokena CSRF” dla żądań urządzeń json

82

Jak mogę pobrać token CSRF do przekazania z żądaniem JSON?

Wiem, że ze względów bezpieczeństwa Railsy sprawdzają token CSRF na wszystkich typach żądań (w tym JSON / XML).

Mógłbym włożyć kontroler skip_before_filter :verify_authenticity_token, ale straciłbym ochronę CRSF (nie wskazane :-)).

Ta podobna (wciąż nie akceptowana) odpowiedź sugeruje

Pobierz token za pomocą <%= form_authenticity_token %>

Pytanie brzmi jak? Czy muszę najpierw wywołać którąkolwiek z moich stron, aby pobrać token, a następnie przeprowadzić prawdziwe uwierzytelnienie za pomocą Devise? Czy jest to jednorazowa informacja, którą mogę pobrać z mojego serwera, a następnie konsekwentnie używać (do momentu ręcznej zmiany jej na samym serwerze)?

Społeczność
źródło

Odpowiedzi:

127

EDYTOWAĆ :

W Rails 4 używam teraz tego, co @genkilabs sugeruje w komentarzu poniżej:

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

Co zamiast całkowicie wyłączyć wbudowane zabezpieczenia, zabija każdą sesję, która może istnieć, gdy coś trafia na serwer bez tokena CSRF.


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

Spowoduje to wyłączenie sprawdzania CSRF dla postów / pozycji json, które zostały poprawnie oznaczone jako takie.

Na przykład w iOS ustawiasz następujące żądanie w NSURLRequest, gdzie „parametry” są parametrami:


[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]]];
Ryan Crews
źródło
3
robiąc to, jesteś narażony na atak, jeśli atakujący oznaczy format jako „json”. I właśnie dlatego Railsy z definicji wydają ostrzeżenie. Chcę, aby mój json mógł poprawnie przekazać token, w przeciwnym razie ostrzeżenie w dziennikach jest w porządku.
21
jesteś narażony na ataki, dlatego zalecają włączenie innych zabezpieczeń podczas wyłączania filtra. Zwykle w przypadku żądań API żądający wysyła klucz API wraz z danymi wiadomości, które następnie weryfikujesz przed wykonaniem żądanej metody.
Ryan Crews
18
W szynach 4 możesz zrobić coś takiego:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
genkilabs
2
@genkilabs Podoba mi się, dodano do odpowiedzi dla użytkowników rails 4, dzięki =]
Ryan Crews,
1
Dwie rzeczy, które właśnie zrobiłem, aby to zadziałało w mojej aplikacji Rails i iOS: 1) Nie widziałem żadnego powodu, dla którego mieszaliśmy składnię hash starego i nowego stylu w Rubim, więc kod w moim kontrolerze wygląda następująco: protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2) Niepowiązane z tym pytaniem, ale związane z tematem POSTING JSON z iOS, ponieważ używam AFNetworkingi Railsy nie manager.requestSerializer = [AFJSONRequestSerializer serializer];
pakują
18

Możesz wysłać token CSRF po pomyślnym zalogowaniu, używając niestandardowego nagłówka.

Np. Umieść to w swoich sesjach # create:

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

Przykładowy nagłówek odpowiedzi logowania zawierający 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

Ten token jest ważny do momentu ponownego zalogowania lub (wyloguj się, jeśli obsługujesz to za pośrednictwem swojego interfejsu API). Twój klient może wyodrębnić i zapisać token z nagłówków odpowiedzi logowania. Następnie każde żądanie POST / PUT / DELETE musi ustawić nagłówek X-CSRF-Token z wartością otrzymaną w czasie logowania.

Przykładowe nagłówki POST z tokenem 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=

Dokumentacja: form_authenticity_token

tasos
źródło
18

Naprawdę najprostszy sposób. Nie przejmuj się zmianą nagłówków.

Upewnij się że masz:

<%= csrf_meta_tag %>

w Twoim layouts/application.html.erb

Po prostu wykonaj ukryte pole wejściowe, takie jak:

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

Lub jeśli chcesz wysłać post w 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);
    }
});

Zasadniczo, kiedy publikujesz swoje dane json, po prostu dodaj prawidłowe pole autentyczności_token do postdanych, a ostrzeżenie powinno zniknąć ...

Walter Schreppers
źródło
IMHO powinno nie tylko umieścić ostrzeżenie w rejestratorze, ale także całkowicie odrzucić posty bez ważnego tagu csrf domyślnie, po prostu wysyła ostrzeżenie i przesyła dane w porządku ...
Walter Schreppers
Krótszy sposób wykonania pola wejściowego tokena ukrytej autentyczności to = token_tag(nil)(zero jest ważne)
Yo Ludke
W starszych wersjach szyn tag kończy się liczbą mnogą. <% = csrf_meta_tags%>
cyonder
4

Rozwiązałem ten błąd w ten sposób:

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

Źródło: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html

gusjabar
źródło
Nie jestem pewien, czy chcesz to zrobić - przeglądarki mogą używać interfejsów API JSON.
Joe Van Dyk,
Dziękuję Ci! Potrzebowałem szybkiego (i brudnego) sposobu rozwiązania tego problemu, a to może działać wystarczająco dobrze w przypadku tego, co próbuję osiągnąć.
NerdyTherapist
Połączony dokument (teraz zaktualizowany dla Rails 5) wygląda na: protect_from_forgery with: :exception, unless: -> { request.format.json? }i zadziałał dla mnie. Dzięki.
akostadinov
1
To tylko wyłącza sprawdzanie, co pozwala uniknąć problemu. Jeśli i aplikacja ma to wyłączyć, jaki jest sens włączonej ochrony przed fałszerstwem?
jefflunt
3

Niepokojące jest to, że w Railsach 3.2.3 otrzymujemy teraz ostrzeżenie CSRF w pliku production.log, ale post nie zawodzi! Chcę, żeby to się nie udało, ponieważ chroni mnie przed atakami. I możesz dodać token csrf za pomocą jquery przed filtrem btw:

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

Walter Schreppers
źródło
2

Użyłem poniżej. Używasz include? więc jeśli typ zawartości to application / json; charset = utf-8, to nadal działa.

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format.include? 'application/json' }
DaCart
źródło
2

Ta odpowiedź jest lepsza.

Możesz zachować walidację CSRF-TOKEN bez dodatkowego wysiłku (token jest dołączany) przed wysłaniem XMLHttpRequest. Bez JQuery, nic po prostu skopiuj / wklej i odśwież.

Po prostu dodaj ten kod.

(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);
    };
}());
Emanuel
źródło
1

Miałem ten sam problem z następującą wersją Railsów:
gem 'rails',: git => 'git: //github.com/rails/rails.git',: branch => '3-2-stable'

Zaktualizowałem do wersji 3.2.2 i teraz wszystko działa dobrze. :)
gem 'rails', '3.2.2'

Olivier Grimard
źródło
dzieki za sugestie. Wypróbowałem to, ale nadal mam to samo ostrzeżenieWARNING: Can't verify CSRF token authenticity
0

Dziś wieczorem napotkałem ten sam problem. Dzieje się tak, ponieważ po zalogowaniu ostatni token csrf nie jest już ważny. To, co zrobiłem, to: $("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>'); w twojej aplikacji / views / devise / session / create.js.rb.

Teraz ma prawidłowy token csrf :) Mam nadzieję, że to pomoże

nbit001
źródło
0

Również w trybie programowania / testowania.

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

To ostrzeżenie pojawia się, ponieważ używasz :null_session, w Railsach 4.1 działa domyślnie, jeśli nie with:podano opcji.

protect_from_forgery
merqlove
źródło