Pour une API REST, il semble qu'il suffit de vérifier la présence d'un en-tête personnalisé pour se protéger contre les attaques CSRF, par ex. le client envoie
"X-Requested-By: n'importe quoi"
et le serveur vérifie la présence de "X-Requested-By" et abandonne la demande si l'en-tête n'est pas trouvé. La valeur de l'en-tête n'est pas pertinente. Voici comment fonctionne CsrfProtectionFilter de Jersey 1.9 et il est décrit dans cet article de blog: http://blog.alutam.com/2011/09/14/jersey-and-cross-site -request-forgery-csrf/ . Le blog contient également des liens vers NSA et des articles de Stanford indiquant que l'en-tête personnalisé lui-même est une protection suffisante:
La première méthode consiste à définir des en-têtes personnalisés pour chaque REST demande telle que X-XSRF-Header. La valeur de cet en-tête n'a pas d'importance; la simple présence devrait empêcher les attaques CSRF. Si une demande arrive dans un point de terminaison REST sans l'en-tête personnalisé, la demande doit être supprimée.
Les requêtes HTTP d'un navigateur Web effectuées via le formulaire, l'image, l'iframe, etc. ne peuvent pas définir d'en-têtes HTTP personnalisés. La seule façon de créer une demande HTTP à partir d'un navigateur avec un en-tête HTTP personnalisé est d'utiliser une technologie telle que Javascript XMLHttpRequest ou Flash. Ces technologies peuvent définir des en-têtes HTTP personnalisés, mais ont des politiques de sécurité intégrées pour empêcher les sites Web de s'envoyer des requêtes, sauf autorisation expresse de la politique. Cela signifie qu'un site Web www.bad.com ne peut pas envoyer de demande à http://bank.example.com avec l'en-tête personnalisé X-XSRFHeader sauf si ils utilisent une technologie telle que XMLHttpRequest. Cette technologie empêcherait qu'une telle demande soit faite à moins que le domaine bank.example.com ne l'autorise spécifiquement. Il en résulte alors un point de terminaison REST qui ne peut être appelé que via XMLHttpRequest (ou une technologie similaire).
Il est important de noter que cette méthode empêche également tout accès direct d'un navigateur Web à ce point de terminaison REST. Les applications Web utilisant cette approche devront s'interfacer avec leurs points de terminaison REST via XMLHttpRequest ou une technologie similaire.
Source: Lignes directrices pour la mise en œuvre de REST
Il semble cependant que la plupart des autres approches suggèrent que vous devez générer un jeton et également le valider sur le serveur. Est-ce une ingénierie excessive? Quand une approche "présence de" serait-elle sécurisée, et quand la validation des jetons est-elle également requise?
La sécurité, c'est une défense en profondeur. Une simple vérification de la valeur est suffisante pour le moment , mais les technologies et attaques futures peuvent être exploitées pour briser votre protection. Tester la présence d'un jeton atteint la défense minimale absolue nécessaire pour faire face aux attaques actuelles. L'ajout du jeton aléatoire améliore la sécurité contre les vecteurs d'attaque potentiels . L'utilisation d'un jeton par demande permet également de limiter les dommages causés par une vulnérabilité XSS, car l'attaquant a besoin d'un moyen de voler un nouveau jeton pour chaque demande qu'il fait.
C'est le même raisonnement utilisé dans les algorithmes cryptographiques modernes, où n
tours sont considérés comme un minimum pour la sécurité, mais 2n+1
les tours (par exemple) sont choisis dans la mise en œuvre officielle pour assurer une marge de sécurité décente.
Lectures complémentaires:
TL; DR - Vérifier l'existence d'un en-tête non standard comme "X-Requested-By" devrait être suffisant pour se prémunir contre les attaques CSRF sans vérifier la valeur de l'en-tête.
Le site du framework Play le décompose vraiment bien:
Autrement dit, un attaquant peut contraindre un navigateur victime à effectuer les types de demandes suivants:
- Toutes les demandes GET
- Demandes POST avec des corps de type application/x-www-form-urlencoded, multipart/form-data et text/plain
Un attaquant ne peut pas:
- Obliger le navigateur à utiliser d'autres méthodes de demande telles que PUT et DELETE
- Obliger le navigateur à publier d'autres types de contenu, tels que application/json
- Obliger le navigateur à envoyer de nouveaux cookies, autres que ceux que le serveur a déjà définis
- Obliger le navigateur à définir des en-têtes arbitraires, autres que les en-têtes normaux que le navigateur ajoute aux demandes
Cela a du sens si vous considérez les vecteurs d'attaque pour CSRF:
JavaScript est soumis à la politique de même origine , il ne peut donc ajouter des en-têtes non standard que si l'une des conditions suivantes est remplie:
Des en-têtes non standard peuvent être définis lors d'une attaque XSS. L'utilisation d'un en-tête non standard pour empêcher les attaques CSRF ne rend pas un site plus (ou moins) vulnérable aux attaques XSS quelle que soit la valeur de l'en-tête. Les en-têtes non standard et les jetons CSRF sont vulnérables aux attaques XSS. Si l'attaquant XSS peut définir un en-tête non standard sur une demande (par exemple XHR dans le domaine), il peut certainement accéder à un jeton CSRF défini dans un cookie ou intégré dans DOM ou dans une variable JavaScript.
Il y a une SO question ici qui est déroutante mais est arrivée à la même conclusion.
Quelques exemples de ces en-têtes non standard dans la nature:
EDIT: ce csrf-request-builder exploitait une vulnérabilité dans Flash, qui a maintenant été corrigée. Il est possible d'envoyer des requêtes complexes avec JavaScript, cependant si vous spécifiez des éléments d'en-tête supplémentaires, une requête HTTP OPTIONS de contrôle en amont sera envoyée avant la requête réelle.
J'ai vérifié que Jersey est vulnérable à CSRF et les développeurs de Jersey ont été informés. Il est possible de tirer parti de cette vulnérabilité à l'aide de Flash et éventuellement d'autres technologies de script. Jersey est vulnérable car l'en-tête HTTP "X-Requested-By" n'est pas sur liste noire des en-têtes de flash .
J'ai utilisé le CSRF-Request-Builder avec les arguments suivants pour créer une demande de publication:
file://var/code/CSRF-Request-Builder/csrf_payload.html#url=http://google.com&X-Requested-By=1&body={'test':1}
Vous ne devriez jamais venir avec votre propre méthode de prévention de la CSRF à moins que vous compreniez vraiment l'exploitation de la CSRF. Le CSRF Prevention Cheat sheet est une excellente ressource.
https://stackoverflow.com/a/11423778/14731 fait un point très important: la même politique d'origine (SOP) vise à empêcher la lecture des réponses interdomaines, pas avec l'écriture des demandes.
Cela signifie que même si vous pourrez peut-être écrire des en-têtes personnalisés à l'avenir, il est extrêmement improbable que vous puissiez jamais lire la réponse d'une demande interdomaine. En tant que tel, les meilleures protections CSRF impliquent la lecture d'une valeur secrète du serveur, sa réécriture et la validation par le serveur de la valeur.
Vous n'avez pas nécessairement besoin d'un état côté serveur pour accomplir cela ( Double-Submit Cookies , et Encrypted Token Pattern en sont deux exemples) mais vous devez valider une valeur secrète sur le serveur.
À partir de 2020, MDN recommande désormais explicitement `` Pour empêcher les écritures entre origines, vérifiez un jeton non devinable dans la demande - appelé jeton CSRF (Cross-Site Request Forgery). Voir https://developer.mozilla.org/en-US/docs/Web/Security/Same-Origin_policy (faites défiler jusqu'à 'Comment bloquer l'accès entre les origines croisées').