J'ai une application API Rails 5 (ApplicationController < ActionController::API
). La nécessité d'ajouter un simple formulaire d'interface graphique pour un noeud final de cette API est apparue.
Au début, j'obtenais ActionView::Template::Error undefined method protect_against_forgery?
lorsque j'ai essayé de rendre le formulaire. J'ai ajouté include ActionController::RequestForgeryProtection
et protect_from_forgery with:exception
à ce terminal. Ce qui a résolu le problème comme prévu.
Cependant, lorsque j'essaie de soumettre ce formulaire, je reçois: 422
Unprocessable Entity
ActionController::InvalidAuthenticityToken
. J'ai ajouté <%= csrf_meta_tags %>
et vérifié que meta: csrf-param
et meta: csrf-token
sont présents dans mes en-têtes et que authenticity_token
est présent dans mon formulaire. (Les jetons eux-mêmes sont différents les uns des autres.)
J'ai essayé, protect_from_forgery prepend: true, with:exception
, sans effet. Je peux "résoudre" ce problème en commentant: protect_from_forgery with:exception
. Mais je crois comprendre que cela désactive la protection des CSRF dans mon formulaire. (Je veux une protection CSRF.)
Qu'est-ce que je rate?
METTRE À JOUR:
Pour que ce soit clair, 99% de cette application est une API JSON RESTful pure. La nécessité d'ajouter une vue HTML et un formulaire à cette application est apparue. Donc pour un contrôleur, je veux activer la protection CSRF complète. Le reste de l'application n'a pas besoin de CSRF et peut rester inchangé.
MISE À JOUR 2:
Je viens de comparer la source de la page du formulaire HTML et de l'en-tête de cette application avec une autre application Rails 5 conventionnelle que j'ai écrite. Le authenticity_token
dans l'en-tête et le authenticity_token
dans le formulaire sont identiques. Dans l'application API avec laquelle je rencontre un problème, ils sont different. Peut-être que c'est quelque chose?
MISE À JOUR 3:
Ok, je ne le fais pas la disparité est la question. Cependant, lors de comparaisons ultérieures entre les applications actives et non actives, j'ai constaté qu'il n'y avait rien dans Réseau> Cookies. Je vois un tas de choses comme _my_app-session
dans les cookies de l'application active.
Voici le problème: Rails 5, en mode API, n'inclut pas logiquement le middleware Cookie. Sans lui, il n'y a pas de Session key
stockée dans un cookie à utiliser lors de la validation du jeton que j'ai transmis avec mon formulaire.
De manière quelque peu déroutante, changer les choses dans config/initializers/session_store.rb
n'a eu aucun effet.
J'ai finalement trouvé la réponse à ce problème ici: Ajout du magasin de sessions de cookies à l'application Rails API , qui m'a conduit ici: https://github.com/Rails/rails/pull/28009/files qui mentionnait exactement les lignes que je devais ajouter à application.rb pour récupérer les cookies:
config.session_store :cookie_store, key: "_YOUR_APP_session_#{Rails.env}"
config.middleware.use ActionDispatch::Cookies # Required for all session management
config.middleware.use ActionDispatch::Session::CookieStore, config.session_options
Ces trois lignes associées à:
class FooController < ApplicationController
include ActionController::RequestForgeryProtection
protect_from_forgery with: :exception, unless: -> { request.format.json? }
...
Et bien sûr, un formulaire généré par les assistants appropriés:
form_tag(FOO_CREATE_path, method: :post)
...
M'a obtenu un formulaire protégé par CSRF au milieu de mon application API Rails.
Si vous utilisez le mode API Rails 5, vous n'utilisez pas protect_from_forgery
ni n'incluez <%= csrf_meta_tags %>
dans aucune vue, car votre API est "sans état". Si vous envisagiez d'utiliser des Rails complets (pas le mode API) tout en l'utilisant ÉGALEMENT comme API REST pour d'autres applications/clients, vous pouvez procéder de la manière suivante:
protect_from_forgery unless: -> { request.format.json? }
Donc, ce protect_from_forgery
serait appelé le cas échéant. Mais je vois ActionController::API
dans votre code, ce qui indique que vous utilisez le mode API, auquel cas vous supprimez la méthode de votre contrôleur d'application.
Pas besoin de protect_from_forgery pour les appels et les apis AJAX.
Si vous voulez le désactiver pour une action, alors
protect_from_forgery except: ['action_name']
class Api::ApiController < ApplicationController
skip_before_action :verify_authenticity_token
end
Utilisez comme ci-dessus avec Rails 5