J'ai sauté la demande avec Proxy ELB-> Nginx-> ELB-> Taget Group-> ECS sur AWS et j'ai exécuté le service Rails, mais j'ai eu une erreur avec les contre-mesures de jeton CSRF, alors je suis allé au débogage et le résoudre.
L'erreur qui se produisait était ʻActionController :: InvalidAuthenticityToken`.
https://railsguides.jp/security.html#クロスサイトリクエストフォージェリ-csrf Il s'agit d'une mesure de sécurité fournie de série par Rails. Vérifiez que le jeton stocké dans la session correspond à ʻauthencity_token` au POST, et lancez une erreur s'ils ne correspondent pas.
Ajoutez proxy_set_header X-Forwarded-SSL on;
à nginx.conf.
nginx.conf
#C'est en fait écrit, mais omis
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;
#Ajoute ça
proxy_set_header X-Forwarded-SSL on;
}
Vérifiez que le token stocké dans la session correspond à ʻauthencity_token` au POST, et lancez une erreur s'ils ne correspondent pas
Ensuite, j'ai regardé le code Rails qui est réellement vérifié en me demandant si le jeton stocké dans la session et ʻauthencity_token` sont différents, ou si cela peut arriver par un fonctionnement normal.
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
Lorsque cette vérification_requête?
Est fausse, l'erreur ʻInvalidAuthenticityToken` est générée.
Si les jetons sont différents, cela signifie que «any_authenticity_token_valid?» Est faux, donc j'ai essayé de déboguer avec cette attente. Cependant, «any_authenticity_token_valid?» Était vrai.
valid_request_origin?
Est faux?En regardant le code ci-dessus, même si valid_request_origin?
Est faux, vérifié_request?
Peut être faux, alors je l'ai vérifié.
Alors, sûrement, valid_request_origin?
Était faux.
Jetez un œil au contenu de valid_request_origin?
.
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
Pour rendre valid_request_origin?
Faux, j'ai essayé de le sortir car il semble que la raison puisse être comprise si le contenu de request.origin
et request.base_url
est connu.
Ensuite, request.origin
était https: // ~
, tandis que request.base_url
était http: // ~
.
En d'autres termes, il s'est avéré que c'était faux dans la partie vérification de request.origin == request.base_url
dans le code ci-dessus.
À ce stade, j'ai beaucoup enquêté et j'ai trouvé que "Lorsqu'une requête est transmise de Nginx à Rails, même si vous accédez à Nginx via HTTPS, elle semble être transmise à Rails en tant que HTTP. Pour éviter cela, utilisez X-Forwarded-Proto
dans la configuration Nginx. Utilisez pour indiquer à Rails qu'il s'agit de HTTPS. Je l'ai essayé.
nginx.conf
#C'est en fait écrit, mais omis
server {
listen 80;
server_name hoge.jp;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#Ajoute ça
proxy_set_header X-Forwarded-Proto https;
}
Mais ça n'a pas marché. Quand j'ai essayé de sortir l'en-tête de la requête avec Rails, c'était "" X-Forwarded-Proto ":" http "`.
** C'est vrai, cela était dû à la nature de l'ELB. ** ** https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html
Cette fois, une demande est envoyée depuis l'ELB qui est le proxy vers l'ELB associé au service Rails en cours d'exécution, mais elle est envoyée par HTTP.
Application Load Balancer et Classic Load Balancer priorisent les en-têtes de connexion à partir des demandes d'entrée client après avoir transmis par proxy les réponses aux clients </ b>
En conséquence, la communication HTTP entre Nginx et ELB a été priorisée et les en-têtes de requête «X-Forwarded-For», «X-Forwarded-Proto» et «X-Forwarded-Port» ont été réécrits.
L'objet de requête semble avoir été créé avec Rack, j'ai donc jeté un œil au code.
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
#réduction
def base_url
"#{scheme}://#{host_with_port}"
end
https://github.com/rack/rack/blob/649c72bab9e7b50d657b5b432d0c205c95c2be07/lib/rack/request.rb
De la façon dont base_url
est créé, schema
doit être https
.
Il y a certaines conditions pour que schema
soit https
, mais cette fois il semble que get_header (HTTP_X_FORWARDED_SSL) == 'on'
devrait être défini!
(HTTP_X_FORWARDED_SSL
n'a pas besoin d'être réécrit en ELB)
Donc, j'ai ajouté X_Forwarded_SSL
à l'en-tête de requête de Nginx.
nginx.conf
#C'est en fait écrit, mais omis
server {
listen 80;
server_name hoge.jp;
proxy_set_header Host $host;
#Les deux derniers seront réécrits en ELB
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
#Ajoute ça
proxy_set_header X-Forwarded-SSL on;
}
Plus d'erreurs!
Lorsque j'ai débogué, X_Forwarded_SSL
a été ajouté à l'en-tête de la demande et schema
est devenu https
.