web-dev-qa-db-fra.com

Rails Conception d'API sans désactiver la protection CSRF

En février 2011, Rails a été changé en nécessite le jeton CSRF pour tout non-GET requêtes, même celles d'un point de terminaison d'API. Je comprends l'explication de la raison pour laquelle il s'agit d'un changement important pour les requêtes du navigateur, mais cet article de blog ne donne aucun conseil sur la manière dont une API doit gérer le changement.

Je ne souhaite pas désactiver la protection CSRF pour certaines actions.

Comment les API sont-elles censées gérer ce changement? L'espoir est-il qu'un client API fasse une demande GET à l'API pour obtenir un jeton CSRF, puis inclut ce jeton dans chaque demande au cours de cette session?

Il semble que le jeton ne change pas d'un POST à un autre. Est-il sûr de supposer que le jeton ne changera pas pendant la durée de la session?

Je ne savoure pas la gestion supplémentaire des erreurs lorsque la session expire, mais je suppose que c'est mieux que d'avoir à obtenir un jeton avant chaque demande POST/PUT/DELETE.

44
Steve Madsen

Vieille question, mais la sécurité est suffisamment importante pour que je pense qu'elle mérite une réponse complète. Comme discuté dans cette question il y a toujours un risque de CSRF même avec les API. Oui, les navigateurs sont censés se prémunir contre cela par défaut, mais comme vous n'avez pas un contrôle complet du navigateur et des plugins que l'utilisateur a installés, cela devrait toujours être considéré comme une meilleure pratique pour se protéger contre CSRF dans votre API.

La façon dont je l'ai vu parfois consiste à analyser la balise Meta CSRF à partir de la page HTML elle-même. Je n'aime pas vraiment cela, car il ne correspond pas bien à la façon dont de nombreuses applications à page unique + API fonctionnent aujourd'hui et je pense que le jeton CSRF devrait être envoyé dans chaque demande, qu'il s'agisse de HTML, JSON ou XML.

Je suggère donc de passer un jeton CSRF en tant que cookie ou valeur d'en-tête via un filtre après pour toutes les demandes. L'API peut simplement renvoyer cela en tant que valeur d'en-tête de X-CSRF-Token que Rails vérifie déjà.

Voici comment je l'ai fait avec AngularJS:

  # In my ApplicationController
  after_filter :set_csrf_cookie

  def set_csrf_cookie
    if protect_against_forgery?
      cookies['XSRF-TOKEN'] = form_authenticity_token
    end
  end

AngularJS recherche automatiquement un cookie nommé XSRF-TOKEN mais n'hésitez pas à le nommer comme bon vous semble. Ensuite, lorsque vous soumettez un POST/PUT/DELETE, vous devez définir la propriété d'en-tête X-CSRF-Token que Rails recherche automatiquement.

Malheureusement, AngualrJS renvoie déjà le XSRF-TOKEN cookie dans une valeur d'en-tête de X-XSRF-TOKEN. Il est facile de remplacer le comportement par défaut de Rails pour l'adapter à ApplicationController comme ceci:

  protected

  def verified_request?
    super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
  end

Pour Rails 4.2 il y a maintenant un assistant intégré pour valider CSRF qui devrait être utilisé.

  protected

  def verified_request?
    super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
  end

J'espère que c'est utile.

EDIT: Dans un discussion à ce sujet pour une Rails pull-request j'ai soumis qu'il est apparu que le passage du jeton CSRF via l'API pour la connexion est une pratique particulièrement mauvaise ( par exemple, quelqu'un pourrait créer une connexion tierce pour votre site qui utilise des informations d'identification d'utilisateur au lieu de jetons.) Donc, cavet emptor. C'est à vous de décider dans quelle mesure vous êtes préoccupé par cela pour votre application. approche, mais renvoyez uniquement le cookie CSRF à un navigateur qui a déjà une session authentifiée et pas pour chaque demande. Cela empêchera de soumettre une connexion valide sans utiliser la balise Meta CSRF.

48
Chris Nicola

Rails fonctionne avec la convention "sécurisé par défaut". La contrefaçon de demande intersite ou intersession nécessite qu'un utilisateur dispose d'un navigateur et d'un autre site Web de confiance. Cela n'est pas pertinent pour les API, car elles ne s'exécutent pas dans le navigateur et ne conservent aucune session. Par conséquent, vous devez désactiver CSRF pour les API.

Bien sûr, vous devriez protéger votre API en exigeant une authentification HTTP ou un jeton d'API personnalisé ou une solution OAuth).

6
Ariejan