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.
Der aufgetretene Fehler war "ActionController :: InvalidAuthenticityToken".
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.
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;
}
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.
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.
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".
** 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
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.
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;
}
Keine Fehler mehr! Beim Debuggen wurde "X_Forwarded_SSL" zum Anforderungsheader hinzugefügt und "Schema" wurde zu "https".