Rails - "WARNUNG: CSRF-Token-Authentizität kann nicht überprüft werden" für JSON-Entwicklungsanforderungen

81

Wie kann ich das CSRF-Token abrufen, das mit einer JSON-Anforderung übergeben werden soll?

Ich weiß, dass Rails aus Sicherheitsgründen das CSRF-Token für alle Anforderungstypen (einschließlich JSON / XML) überprüft .

Ich könnte meinen Controller einsetzen skip_before_filter :verify_authenticity_token, aber ich würde den CRSF-Schutz verlieren (nicht ratsam :-)).

Diese ähnliche (immer noch nicht akzeptierte) Antwort schlägt vor

Rufen Sie den Token mit ab <%= form_authenticity_token %>

Die Frage ist wie? Muss ich eine meiner Seiten zum ersten Mal aufrufen, um das Token abzurufen, und dann meine echte Authentifizierung mit Devise durchführen? Oder handelt es sich um eine einmalige Information, die ich von meinem Server abrufen und dann konsistent verwenden kann (bis ich sie manuell auf dem Server selbst ändere)?

Gemeinschaft
quelle

Antworten:

127

EDIT :

In Rails 4 verwende ich jetzt das, was @genkilabs im Kommentar unten vorschlägt:

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

Anstatt die integrierte Sicherheit vollständig auszuschalten, wird jede Sitzung beendet, die möglicherweise vorhanden ist, wenn etwas ohne das CSRF-Token auf den Server gelangt.


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

Dies würde die CSRF-Prüfung für JSON-Posts / Puts deaktivieren, die ordnungsgemäß als solche markiert wurden.

Wenn Sie beispielsweise in iOS Folgendes für Ihre NSURLRequest festlegen, wobei "Parameter" Ihre Parameter sind:


[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
quelle
3
Dabei sind Sie einem Angriff ausgesetzt, wenn der Angreifer das Format als "json" markiert. Und deshalb gibt Rails per Definition eine Warnung aus. Ich möchte, dass mein JSON in der Lage ist, ein Token ordnungsgemäß zu übergeben, andernfalls ist es in Ordnung, die Warnung in den Protokollen zu haben.
21
Sie sind Angriffen ausgesetzt, weshalb empfohlen wird, dass Sie beim Ausschalten des Filters eine andere Sicherheit haben. In der Regel sendet der Anforderer bei API-Anforderungen einen API-Schlüssel zusammen mit den Post-Daten, den Sie dann überprüfen, bevor Sie die gewünschte Methode ausführen.
Ryan Crews
18
In Schienen 4 können Sie etwas tun wie:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
Genkilabs
2
@genkilabs Ich mag das, hinzugefügt, um für Schienen 4 Benutzer zu antworten, danke =]
Ryan Crews
1
Zwei Dinge, die ich gerade getan habe, damit dies in meiner Rails & iOS-App funktioniert: 1) Ich habe keinen Grund gesehen, warum wir in Ruby Hash-Syntax im alten und neuen Stil gemischt haben. Der Code in meinem Controller sieht also so aus: protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2) Unabhängig von dieser Frage, aber im Zusammenhang mit dem Thema POSTing JSON von iOS, habe ich AFNetworkingFolgendes manager.requestSerializer = [AFJSONRequestSerializer serializer];
getan
18

Sie können das CSRF-Token nach erfolgreicher Anmeldung mithilfe eines benutzerdefinierten Headers senden.

Fügen Sie dies beispielsweise in Ihre Sitzungen ein. # Create:

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

Beispiel für einen Anmeldeantwort-Header mit dem CSRF-Token:

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

Dieses Token ist gültig, bis Sie sich erneut anmelden oder (abmelden, wenn Sie dies über Ihre API unterstützen). Ihr Client kann das Token aus den Anmeldeantwort-Headern extrahieren und speichern. Dann muss jede POST / PUT / DELETE-Anforderung den X-CSRF-Token-Header auf den Wert setzen, der zum Zeitpunkt der Anmeldung empfangen wurde.

Beispiel für POST-Header mit dem CSRF-Token:

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=

Dokumentation: form_authenticity_token

Tasos
quelle
18

In der Tat einfachster Weg. Machen Sie sich nicht die Mühe, die Überschriften zu ändern.

Stell sicher dass du hast:

<%= csrf_meta_tag %>

in deinem layouts/application.html.erb

Machen Sie einfach ein verstecktes Eingabefeld wie folgt:

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

Oder wenn Sie einen jquery Ajax-Beitrag wünschen:

$.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);
    }
});

Wenn Sie Ihre JSON-Daten veröffentlichen, fügen Sie den Daten einfach ein gültiges Feld authenticity_token hinzu, postund die Warnung sollte verschwinden ...

Walter Schreppers
quelle
IMHO sollte es nicht nur eine Warnung in den Logger setzen, sondern Beiträge ohne ein gültiges csrf-Tag standardmäßig vollständig verwerfen. Es sendet nur die Warnung und sendet die Daten einwandfrei ...
Walter Schreppers
Der kürzere Weg, um das Eingabefeld für versteckte Authentizitätstoken zu = token_tag(nil)
erstellen,
In älteren Versionen von Schienen endet das Tag im Plural. <% = csrf_meta_tags%>
Cyonder
4

Ich habe diesen Fehler folgendermaßen behoben:

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

Quelle: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html

Gusjabar
quelle
Ich bin mir nicht sicher, ob Sie dies tun möchten - Browser können JSON-APIs verwenden.
Joe Van Dyk
Danke dir! Benötigte einen schnellen (und schmutzigen) Weg, um dieses Problem zu lösen, und dies könnte gut genug für das funktionieren, was ich erreichen möchte.
NerdyTherapist
Das verknüpfte Dokument (jetzt aktualisiert für Rails 5) scheint zu sein: protect_from_forgery with: :exception, unless: -> { request.format.json? }und es hat bei mir funktioniert. Vielen Dank.
Akostadinov
1
Dies deaktiviert nur die Prüfung, wodurch das Problem vermieden wird. Wenn und App dies deaktivieren wird, wozu ist dann der Schutz vor Fälschungen aktiviert?
Jefflunt
3

Was besorgniserregend ist, ist, dass wir in Rails 3.2.3 jetzt die CSRF-Warnung in Production.log erhalten, aber der Beitrag schlägt nicht fehl! Ich möchte, dass es fehlschlägt, da es mich vor Angriffen schützt. Und Sie können das csrf-Token mit jquery hinzufügen, bevor Sie übrigens filtern:

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

Walter Schreppers
quelle
2

Ich habe das folgende verwendet. Verwenden Sie include? Wenn der Inhaltstyp application / json; charset = utf-8 ist, funktioniert er immer noch.

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

Diese Antwort ist besser.

Sie können die CSRF-TOKEN-Validierung ohne zusätzlichen Aufwand (das Token wird angehängt) beibehalten, bevor XMLHttpRequest gesendet wird. Keine JQuery, nichts, nur Kopieren / Einfügen und Aktualisieren.

Fügen Sie einfach diesen Code hinzu.

(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
quelle
1

Ich hatte das gleiche Problem mit der folgenden Version von Rails:
gem 'Rails' ,: git => 'git: //github.com/rails/rails.git' ,: branch => '3-2-Stable'

Ich habe auf 3.2.2 aktualisiert und jetzt funktioniert alles gut für mich. :)
Edelstein 'Schienen', '3.2.2'

Olivier Grimard
quelle
danke für den Vorschlag. Ich habe es ausprobiert, aber ich habe immer noch die gleiche WarnungWARNING: Can't verify CSRF token authenticity
0

Ich bin heute Abend auf dasselbe Problem gestoßen. Der Grund dafür ist, dass die Anmeldung beim letzten csrf-Token nicht mehr gültig ist. Was ich getan habe war: $("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>');in Ihrer App / views / devise / session / create.js.rb.

Jetzt hat es ein gültiges csrf-Token :) Ich hoffe es hilft

nbit001
quelle
0

Auch für den Entwicklungs- / Testmodus.

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

Diese Warnung :null_sessionwird angezeigt , weil Sie in Rails 4.1 standardmäßig arbeiten, wenn keine with:Optionen angegeben sind.

protect_from_forgery
merqlove
quelle