[RUBY] Da bei einer Anfrage über ELB ein CSRF-Gegenmaßnahmenfehler aufgetreten ist, bis zum Debuggen und Auflösen

Hintergrund

Ich habe die Anfrage mit Proxy ELB-> Nginx-> ELB-> Taget Group-> ECS unter AWS übersprungen und den Rails-Dienst ausgeführt, aber ich habe einen Fehler mit CSRF-Token-Gegenmaßnahmen erhalten, also bin ich zum Debuggen und Lösen gegangen.

Ein Fehler tritt aufgrund von CSRF-Token-Gegenmaßnahmen auf

Fehlerübersicht

Der aufgetretene Fehler war "ActionController :: InvalidAuthenticityToken".

Was sind CSRF-Token-Maßnahmen?

https://railsguides.jp/security.html#クロスサイトリクエストフォージェリ-csrf Dies ist eine Sicherheitsmaßnahme, mit der Rails standardmäßig geliefert wird. Stellen Sie sicher, dass das in der Sitzung gespeicherte Token mit dem "authencity_token" beim POST übereinstimmt, und geben Sie einen Fehler aus, wenn sie nicht übereinstimmen.

Lösung

Fügen Sie proxy_set_header X-Forwarded-SSL on; zu nginx.conf hinzu.

nginx.conf


#Es ist tatsächlich geschrieben, aber weggelassen
server {
    listen       80;
    server_name  hoge.jp;
    
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    #Füge das hinzu
    proxy_set_header X-Forwarded-SSL on;
}

Fehlerüberprüfung

Ist das Token anders?

Stellen Sie sicher, dass das in der Sitzung gespeicherte Token mit dem "authencity_token" beim POST übereinstimmt, und geben Sie einen Fehler aus, wenn sie nicht übereinstimmen

Dann habe ich mir den Rails-Code angesehen, der tatsächlich überprüft wird, indem ich mich gefragt habe, ob das in der Sitzung gespeicherte Token und das "authencity_token" unterschiedlich sind oder ob dies im normalen Betrieb geschehen kann.

rails/actionpack/lib/action_controller/metal/request_forgery_protection.rb


def verified_request? # :doc:
!protect_against_forgery? || request.get? || request.head? ||
  (valid_request_origin? && any_authenticity_token_valid?)
end

https://github.com/rails/rails/blob/98a4c0c76938e46009cca668da9c3b584a9e9e74/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L289-L292

Wenn diese "verifizierte_Anforderung?" Falsch ist, wird ein "InvalidAuthenticityToken" -Fehler ausgelöst. Wenn die Token unterschiedlich sind, bedeutet dies, dass "any_authenticity_token_valid" falsch ist, also habe ich versucht, mit dieser Erwartung zu debuggen. Aber "any_authenticity_token_valid?" War wahr.

Ist "valid_request_origin" falsch?

Selbst wenn "valid_request_origin?" Falsch ist, kann "verified_request?" Falsch sein, also habe ich es überprüft. Dann war "valid_request_origin?" Sicherlich falsch.

Schauen Sie sich den Inhalt von valid_request_origin? An.

rails/actionpack/lib/action_controller/metal/request_forgery_protection.rb


def valid_request_origin? # :doc:
    if forgery_protection_origin_check
      # We accept blank origin headers because some user agents don't send it.
      raise InvalidAuthenticityToken, NULL_ORIGIN_MESSAGE if request.origin == "null"
      request.origin.nil? || request.origin == request.base_url
    else
      true
    end
end

https://github.com/rails/rails/blob/98a4c0c76938e46009cca668da9c3b584a9e9e74/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L455-L463

Um valid_request_origin? False zu machen, habe ich versucht, es auszugeben, da es den Anschein hat, dass der Grund verstanden werden kann, wenn der Inhalt von request.origin und request.base_url bekannt ist. Dann war "request.origin" "https: // ~", während "request.base_url" "http: // ~" war.

Mit anderen Worten, es stellte sich heraus, dass der Überprüfungsteil von "request.origin == request.base_url" im obigen Code falsch ist.

Setze X-Forwarded-Proto in Nginx conf?

Zu diesem Zeitpunkt ergaben verschiedene Untersuchungen, dass "eine Anfrage, die von Nginx an Rails übergeben wird, auch wenn auf Nginx über HTTPS zugegriffen wird, als HTTP an Rails weitergeleitet zu werden scheint. Um dies zu verhindern, wird" X-Forwarded-Proto "in der Nginx-Konfiguration verwendet Verwenden Sie diese Option, um Rails mitzuteilen, dass es sich um HTTPS handelt. " Ich versuchte es.

nginx.conf


#Es ist tatsächlich geschrieben, aber weggelassen
server {
    listen       80;
    server_name  hoge.jp;
    
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #Füge das hinzu
    proxy_set_header X-Forwarded-Proto https;
}

Aber es hat nicht funktioniert. Als ich versuchte, den Anforderungsheader mit Rails auszugeben, war es "X-Forwarded-Proto": "http".

Wird es irgendwo von https nach http überschrieben?

** Das stimmt, das lag an der Natur von ELB. ** ** ** https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html image.png

Dieses Mal wird eine Anforderung von der ELB, die der Proxy ist, an die ELB gesendet, die dem ausgeführten Rails-Dienst zugeordnet ist. Diese Anforderung wird jedoch über HTTP gesendet.

Application Load Balancer und Classic Load Balancer priorisieren Verbindungsheader aus Client-Eingabeanforderungen, nachdem Antworten an Clients weitergeleitet wurden </ b>

Infolgedessen wurde die HTTP-Kommunikation zwischen Nginx und ELB priorisiert und die Anforderungsheader "X-Forwarded-For", "X-Forwarded-Proto" und "X-Forwarded-Port" wurden neu geschrieben.

Was ist dann zu tun

Das Anforderungsobjekt scheint mit Rack erstellt worden zu sein, also habe ich mir den Code dort angesehen.

rack/lib/rack/request.rb


def scheme
if get_header(HTTPS) == 'on'
  'https'
elsif get_header(HTTP_X_FORWARDED_SSL) == 'on'
  'https'
elsif forwarded_scheme
  forwarded_scheme
    else
      get_header(RACK_URL_SCHEME)
    end
end
      
#Kürzung

def base_url
    "#{scheme}://#{host_with_port}"
end

https://github.com/rack/rack/blob/649c72bab9e7b50d657b5b432d0c205c95c2be07/lib/rack/request.rb

Aus der Art und Weise, wie "base_url" erstellt wird, sollte "schema" "https" sein. Es gibt einige Bedingungen für "Schema" als "https", aber dieses Mal scheint es, dass "get_header (HTTP_X_FORWARDED_SSL) ==" on "gesetzt werden sollte! (HTTP_X_FORWARDED_SSL muss nicht in ELB umgeschrieben werden) Also habe ich X_Forwarded_SSL zum Anforderungsheader von Nginx hinzugefügt.

nginx.conf


#Es ist tatsächlich geschrieben, aber weggelassen
server {
    listen       80;
    server_name  hoge.jp;
    
    proxy_set_header Host $host;
    #Die beiden unteren werden in ELB umgeschrieben
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    #Füge das hinzu
    proxy_set_header X-Forwarded-SSL on;
}

Ergebnis

Keine Fehler mehr! Beim Debuggen wurde "X_Forwarded_SSL" zum Anforderungsheader hinzugefügt und "Schema" wurde zu "https". image.png

Recommended Posts

Da bei einer Anfrage über ELB ein CSRF-Gegenmaßnahmenfehler aufgetreten ist, bis zum Debuggen und Auflösen
Überprüfungswertfehler, der in der Clusterumgebung aufgetreten ist