Dans de nombreux didacticiels et guides, je constate qu'un jeton CSRF doit être actualisé par demande. Ma question est pourquoi dois-je faire cela? Un seul jeton CSRF par session n'est-il pas beaucoup plus facile que d'en générer un par demande et de garder une trace de ceux demandés?
La génération du jeton par demande ne semble pas améliorer la sécurité au-delà de ce qu'un jeton par session ferait déjà. Le seul argument semble être la protection XSS, mais cela ne s'applique pas car lorsque vous avez une vulnérabilité XSS, le script peut quand même lire de nouveaux jetons.
Quels sont les avantages de générer de nouveaux jetons par demande?
Pour les raisons déjà évoquées, il n'est pas nécessaire de générer un nouveau jeton par requête. Cela n'apporte presque aucun avantage en termes de sécurité et cela vous coûte en termes de convivialité: avec un seul jeton valide à la fois, l'utilisateur ne pourra pas naviguer normalement dans la webapp. Par exemple, s'ils frappent le bouton "retour" et soumettent le formulaire avec de nouvelles valeurs, la soumission échouera et les accueillera probablement avec un message d'erreur hostile. S'ils essaient d'ouvrir une ressource dans un deuxième onglet, ils trouveront la session interrompue au hasard dans l'un ou les deux onglets. Il n'est généralement pas utile de mutiler l'utilisabilité de votre application pour satisfaire cette exigence inutile.
Il y a est un endroit où il vaut la peine d'émettre un nouveau jeton CSRF, cependant: lors du changement de principal dans une session. C'est, principalement, lors de la connexion. Il s'agit d'empêcher une attaque de fixation de session conduisant à une possibilité d'attaque CSRF.
Par exemple: un attaquant accède au site et génère une nouvelle session. Ils prennent l'ID de session et l'injectent dans le navigateur d'une victime (par exemple en écrivant un cookie à partir d'un domaine voisin vulnérable, ou en utilisant une autre vulnérabilité comme les URL jsessionid), et injectent également le jeton CSRF sous une forme dans le navigateur de la victime. Ils attendent que la victime se connecte avec ce formulaire, puis utilisent un autre post de formulaire pour amener la victime à effectuer une action avec le jeton CSRF toujours vivant.
Pour éviter cela, invalidez le jeton CSRF et émettez-en un nouveau aux endroits (comme la connexion) que vous faites déjà de même avec l'ID de session pour empêcher les attaques de fixation de session.
Présentation. Le conseil standard consiste à utiliser un jeton CSRF unique qui est unique pour chaque demande. Pourquoi? Parce qu'un jeton par demande est un peu plus résistant à certains types d'erreurs d'implémentation qu'un jeton par session. Cela fait des jetons par demande le meilleur choix pour le développement de nouvelles applications Web. De plus, aucun auditeur de sécurité ne vous tracassera sur l'utilisation d'un jeton CSRF par demande.
Si vous êtes développeur d'applications Web, c'est tout ce que vous devez savoir et vous pouvez arrêter de lire ici. Mais si vous êtes un expert en sécurité et que vous vous interrogez sur la justification détaillée de ces conseils, ou sur l'ampleur du risque si vous utilisez un jeton par session, lisez la suite ...
Creuser un peu plus profondément. La vérité est que, si vous n'avez pas d'autres vulnérabilités dans votre site Web, un seul jeton CSRF par session est OK. Il n'y a aucune raison pour que vous ayez pour générer un nouveau jeton CSRF par demande.
Cela est démontré par le fait que vous trouverez également des experts en sécurité réputés qui disent qu'une autre façon raisonnable de se défendre contre CSRF est d'utiliser la double soumission des cookies: en d'autres termes, vous utilisez du Javascript côté client qui calcule un hachage de la cookie de session et l'ajoute à chaque POST, traitant le hachage comme le jeton CSRF. Vous pouvez voir que cela génère essentiellement à la volée un jeton CSRF identique pour toute la session .
Bien sûr, je connais l'argument pourquoi certaines personnes pourraient recommander de générer un nouveau jeton CSRF pour chaque demande. Ils pensent, si vous avez également une vulnérabilité XSS sur votre site Web, alors si vous utilisez un seul jeton CSRF par session, il sera facile d'utiliser XSS pour récupérer le jeton CSRF, alors que si vous générez un nouveau jeton CSRF par demande, il prendra plus de travail pour récupérer le jeton CSRF. Personnellement, je ne trouve pas cela un argument terriblement convaincant. Si vous avez une vulnérabilité XSS sur votre site, il est toujours possible de récupérer des jetons CSRF même si vous générez un nouveau jeton CSRF pour chaque demande, il suffit de quelques lignes supplémentaires de Javascript malveillant. Quoi qu'il en soit, si vous avez une vulnérabilité XSS sur votre site et que vous faites face à un attaquant sérieux et compétent, il est difficile de garantir la sécurité, quelle que soit la façon dont vous générez vos jetons CSRF.
Dans l'ensemble, cela ne peut pas nuire de générer un nouveau jeton CSRF pour chaque demande. Et peut-être vaut-il mieux le faire de cette façon, juste pour détourner les auditeurs de sécurité. Mais si vous avez déjà une application héritée qui utilise un seul jeton CSRF pour toute la session, dépenser de l'argent pour la convertir pour générer un nouveau jeton CSRF pour chaque demande ne serait probablement pas très élevé dans ma liste de priorités: je parie que je pourrait trouver d'autres utilisations de cet argent et de l'énergie du développeur qui amélioreraient encore plus la sécurité.
XSS peut être utilisé pour lire un jeton CSRF, même s'il s'agit d'un seul jeton de soumission, c'est un jeu d'enfant. Il est probable que cette recommandation d'un seul jeton de soumission provienne de quelqu'un qui ne comprend pas CSRF.
La seule raison d'utiliser un "jeton de soumission unique" est si vous voulez empêcher l'utilisateur de cliquer accidentellement deux fois sur soumettre. Une bonne utilisation de ceci est d'empêcher l'utilisateur de cliquer deux fois sur "checkout" et de facturer accidentellement le client deux fois.
Un CAPTCHA ou demander le mot de passe actuel de l'utilisateur peut être utilisé comme une mesure anti-csrf qui ne peut pas être contournée par XSS.
Je recommande de lire le CSRF Prevention Cheat Sheet .
Si le jeton est le même sur toute la session, il peut être possible pour un attaquant de divulguer un jeton d'une page et de l'utiliser pour une action différente.
Par exemple, vous pouvez utiliser un iframe pour charger une page, puis extraire le jeton à l'aide d'une vulnérabilité XSS. De là, vous pouvez utiliser ce jeton pour soumettre un formulaire de changement de mot de passe
L'utilisation d'un jeton par demande au lieu d'un jeton à l'échelle de la session rend les choses plus difficiles, mais cela n'empêche pas CSRF. Un attaquant peut simplement exploiter un XSS pour lire le jeton depuis la page, puis le tirer. Cependant, si le jeton est global plutôt que limité à cette page individuelle, un attaquant peut cibler n'importe quelle page pour voler le jeton. L'utilisation de jetons séparés pour chaque demande rend cela beaucoup plus difficile.
En plus des autres réponses, il serait peut-être judicieux de rafraîchir le jeton également si votre serveur est sensible à BREACH attack .
Cela nécessite les trois conditions suivantes:
- Être servi à partir d'un serveur qui utilise la compression de niveau HTTP
- Refléter l'entrée utilisateur dans les corps de réponse HTTP
- Refléter un secret (tel qu'un jeton CSRF ) dans les corps de réponse HTTP
Pour atténuer BREACH, vous devez actualiser le jeton CSRF sur la demande GET qui charge un formulaire pour invalider tous les jetons précédents. De cette façon, un MITM (Man-In-The-Middle) créant des demandes supplémentaires pour découvrir le jeton dans la page obtiendra un jeton différent à chaque fois. Cela signifie que l'utilisateur réel ne pourra pas soumettre le formulaire dans une situation MITM.
Bien sûr, vous devez également respecter les deux autres conditions. Il serait probablement plus facile de conserver un jeton CSRF par session et de désactiver la compression de niveau HTTP pour toutes les pages qui servent des formulaires.
Ce n'est pas un fait très connu, mais la comparaison de chaînes normale est vulnérable aux attaques de synchronisation ( comme celle-ci . Longue histoire, opérations de comparaison de chaînes normales (==
ou ===
) comparera deux chaînes caractère par caractère de gauche à droite et renverra false une fois qu'ils auront rencontré un caractère différent à une position donnée dans les deux chaînes. Cela donne une minuscule différence de timing qui a été prouvée être détectable .
En utilisant une attaque temporelle pour essayer chaque personnage possible pour chaque position, il est possible de déterminer quel est le jeton réel. En créant un nouveau jeton à chaque fois qu'une demande avec un jeton est soumise, cette attaque peut être évitée.
Bien sûr, cela ne fonctionne que si vous recréez également le jeton si un jeton non valide a été soumis, ce qui peut être indésirable. Par conséquent, il vaut mieux résoudre ce problème en utilisant une fonction de comparaison de chaînes insensible au temps comme celle-ci .
L'une des raisons pour lesquelles vous souhaitez modifier le jeton CSRF par demande est d'atténuer les fuites de compression du jeton. Ce que je veux dire par là, c'est que si l'attaquant Eve peut injecter des données sur une page contenant le jeton où la page est envoyée compressée, Eve peut alors deviner le premier caractère de la chaîne recevant un ensemble de données plus petit pour la page sachant qu'elle a deviné juste puis aller sur le caractère suivant.
Ceci est similaire à une attaque temporelle.
Pour le moment, il n'y a aucune raison (à ma connaissance) de changer le jeton s'il n'est envoyé que dans les en-têtes HTTP.
Une autre méthode consiste à changer le jeton dès qu'une demande ayant échoué est découverte, mais cela pourrait avoir des problèmes avec l'utilisateur ne pouvant soumettre aucun formulaire.