web-dev-qa-db-fra.com

Exemple d’attaque CSRF (falsification de requêtes intersites) et prévention dans PHP

J'ai un site Web où les gens peuvent voter comme ceci:

http://mysite.com/vote/25

Cela placera un vote sur le point 25. Je souhaite que ceci ne soit disponible que pour les utilisateurs enregistrés, et uniquement s'ils le souhaitent. Maintenant, je sais que quelqu'un est occupé sur le site Web et que quelqu'un leur donne un lien comme celui-ci:

http://mysite.com/vote/30

alors le vote sera une place pour lui sur le point sans qu'il le veuille.

J'ai lu l'explication sur le site Web de l'OWASP , mais je ne la comprends pas vraiment

Est-ce un exemple de CSRF et comment puis-je empêcher cela? La meilleure chose à laquelle je puisse penser consiste à ajouter quelque chose au lien, comme un hachage. Mais ce sera assez irritant de mettre quelque chose au bout de tous les liens. N'y a-t-il pas d'autre moyen de le faire?.

Une autre chose peut-être que quelqu'un peut me donner un autre exemple, parce que le site Web me semble assez fugue.

48
Saif Bechan

Cela pourrait devenir un exemple de CSRF si:

  • ce lien est récupéré (via une balise <img>, par exemple) : faux
  • depuis un autre site: cross-site


Par exemple, si je pouvais injecter cette balise <img> dans le code source HTML de stackoverflow (et je peux le faire, car stackoverflow permet d’utiliser des balises <img> dans ses publications) :

<img src="http://mysite.com/vote/30" />

Vous auriez juste voté pour cet article ;-)


La solution généralement utilisée consiste à placer dans l'URL un jeton dont la durée de vie est limitée dans le temps et à vérifier, lorsque l'URL est extraite, que ce jeton est toujours valide.

L'idée de base serait:

  • Lors de la génération de la page:
    • générer un jeton unique
    • stockez-le dans la session de l'utilisateur
    • et placez-le dans les liens de la page - qui ressemblerait à ceci: http://mysite.com/vote/30?token=AZERTYUHQNWGST
  • Lorsque la page de vote est appelée:
    • Vérifier si le jeton est présent dans l'URL
    • Vérifier s'il est présent dans la session de l'utilisateur
    • Sinon => ne pas enregistrer le vote

L'idée est la suivante:

  • Les jetons n'ont pas une longue durée de vie et sont difficiles à deviner
  • Ce qui signifie que votre attaquant :
    • n'a qu'une fenêtre de quelques minutes pendant lesquelles son injection sera valable
    • devra être bon à deviner ^^
    • devra générer une page différente pour chaque utilisateur.


Notez également que plus la session de l'utilisateur reste active une fois qu'il a quitté votre site, moins il y a de risques qu'elle soit toujours valide lorsqu'il visite le mauvais site.

Mais ici, il faut choisir entre sécurité et convivialité ...


Une autre idée (ce n’est pas parfaitement sûr, mais aider les gars ne sait pas forcer une demande POST) , serait d’accepter uniquement les demandes POST lorsque les gens le sont. vote:

  • Le navigateur envoie des requêtes GET pour les tags injectés
  • Comme cette URL modifie certaines données, elle ne devrait pas fonctionner avec GET, mais uniquement avec POST.

Mais notez que ce n’est pas parfaitement sûr: c’est (probablement?) possible de forcer/forger une demande POST, avec un peu de Javascript.

91
Pascal MARTIN

Tout d’abord, la requête GET ne doit pas être utilisée pour modifier les états sur le serveur. Par conséquent, je recommanderais POST/PUT pour votre service de vote. Ceci est seulement une ligne directrice, mais un couperet. 

Donc, à votre question, CSRF est un problème de client, donc peu importe le type de langue de serveur que vous utilisez (PHP dans votre cas). Le correctif standard est le même et va comme ceci: Avoir une valeur aléatoire dans l'URI/données POST et la même valeur dans l'en-tête Cookie. Si ces correspondances, vous pouvez être sûr qu'il n'y a pas de CSRF. Il y a beaucoup d'informations sur la façon dont cela pourrait être fait ici sur StackOverflow, par exemple. celui-là .
Bonne chance!

18
MyGGaN

OWASP a un CSRFGuard pour PHP et ESAPI pour PHP que j'ai écrit il y a longtemps pour XMB -> UltimaBB -> GaiaBB. 

http://code.google.com/p/gaiabb-olpc/source/search?q=function+get_new_token&origq=function+get_new_token&btnG=Search+Trunk

Il semble que d'autres aient nettoyé ce code et permis des jetons plus forts:

https://www.owasp.org/index.php/PHP_CSRF_Guard

merci, Andrew

5
vanderaj

Il y a 3 joueurs dans une attaque CSRF

  1. le site Web de la victime (votre site de vote dans votre exemple) [ connaît les cookies de ses utilisateurs connectés ]
  2. le navigateur de votre client (lorsqu'il est connecté) [ connaît ses cookies ]
  3. un site Web attaquant [ Ne connaît pas les cookies des utilisateurs connectés ]

Les attaques CSRF dépendent de 2 faits

  1. les navigateurs envoient des cookies automatiquement à chaque demande
  2. nous dépendons des cookies pour identifier nos utilisateurs connectés (par exemple: setcookie("sessionID", "0123456789ABCDEF", time()+3600);)

Si un attaquant pouvait par là ou par un autre, un utilisateur connecté demande ceci

// http://victim.website/vote/30

par exemple, en plaçant le lien sur le site Web de l'attaquant ou en l'envoyant par courrier électronique, le navigateur client connecté enverra les cookies d'identification (sessionID) avec cette demande, ce qui incitera le site Web de la victime à penser que son utilisateur connecté souhaite réellement voter!

Mais si le site Web de la victime est plus intelligent et vérifie les demandes de ses utilisateurs connectés avec un paramètre supplémentaire GET ou POST (pas les cookies), l'attaquant est maintenant dans un problème car les paramètres GET et POST ne sont pas envoyé automatiquement par les navigateurs, et il doit le deviner.

// http://victim.website/vote/30?csrfSecret=0123456789ABCDEF

L'attaquant ne connaît pas le paramètre csrfSecret qui est un secret entre le site Web de la victime et son client (tout comme le jeton de session). Il n'a donc aucun moyen de créer l'URL par laquelle il souhaite forger une requête.

De même, si le vote est effectué à la demande de POST], l'attaquant ne pourra pas créer le formulaire sur son site Web (ou sur un site Web tiers) car il ne connaît pas le secret entre le site Web de la victime et ses utilisateurs.

<form method="post" action="http://victim.website/vote" >
    <input type="hidden" name="vote" value="30">
    <input type="hidden" name="csrfSecret" value="????? I don't know it :(">
</form>
1
Accountant م