PROTECTION CONTRE LES FAILLES CSRF : CROSS SITE REQUEST FORGERIES

winwarrior Messages postés 654 Date d'inscription jeudi 3 avril 2003 Statut Membre Dernière intervention 10 février 2009 - 10 févr. 2009 à 20:13
MacGaliver Messages postés 146 Date d'inscription vendredi 28 mai 2010 Statut Membre Dernière intervention 21 juillet 2013 - 26 avril 2012 à 17:27
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/49233-protection-contre-les-failles-csrf-cross-site-request-forgeries

MacGaliver Messages postés 146 Date d'inscription vendredi 28 mai 2010 Statut Membre Dernière intervention 21 juillet 2013 3
26 avril 2012 à 17:27
Je vais essayer.

Mais rappelons aussi que le .htacces est aussi efficace pour combler certaines failles (XSS, ...).

Cdt.
LPUnderground Messages postés 7 Date d'inscription dimanche 20 juillet 2008 Statut Membre Dernière intervention 20 avril 2010
20 avril 2010 à 05:25
Salut j'ai regardé un peu le code source de ta classe, dans la méthode escapeValue, il serait plus judicieux de supprimer les commandes JavaScript par une fonction de remplacement insensible à la classe, le mieux serait même d'utiliser une regex ; )
kankrelune Messages postés 1293 Date d'inscription mardi 9 novembre 2004 Statut Membre Dernière intervention 21 mai 2015
30 juin 2009 à 00:46
tiens...

function uv_getVar($var_name) {
if (isset($_SERVER[$var_name])) {
return $_SERVER[$var_name];
}
if (isset($_ENV[$var_name])) {
return $_ENV[$var_name];
}
if(($env = @getenv($var_name)) !== false) {
return $env;
}
if (function_exists('apache_getenv')) {
if(($env = @apache_getenv($var_name, true)) !== false) {
return $env;
}
}
return '';
}

function uv_getIP()
{
$proxy_ip = '';
$i = -1;
$where = array(
'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED',
'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED',
'HTTP_VIA', 'HTTP_X_COMING_FROM', 'HTTP_COMING_FROM'
);

while(!empty($proxy_ip) && isset($where[++$i])) {
$proxy_ip = uv_getVar($where[$i]);
}

if (empty($proxy_ip))
return uv_getVar('REMOTE_ADDR');
else
{
$is_ip = preg_match('|^([0-9]{1,3}\.){3,3}[0-9]{1,3}|', $proxy_ip, $regs);
if (!empty($is_ip) && (count($regs) > 0))
return $regs[0];
else
return $proxy_ip;
}
}

@ tchaOo°
kankrelune Messages postés 1293 Date d'inscription mardi 9 novembre 2004 Statut Membre Dernière intervention 21 mai 2015
30 juin 2009 à 00:39
Slt...

- je n'avais pas vu pour le singleton (j'ai pas épluché la source en détail) du coup c'est pas du token dont je parle mais de la clef tokenKey() qui plus est ça fait peut être beaucoup une string de 32 char comme index (ça prend de la place pour rien dans une requête GET) et je ne suis pas sûr que le gain soit énorme niveau sécu

- tu sais comme moi que récupérer l'ip via php (et même tout court) est pas super fiable, d'autant plus quand l'internaute est derrière un proxy ou un réseau cependant tu as des variables transmise par les proxy notament les x_forwarded et http_via qui peuvent être utile si renseignées... tu pourra trouver un exemple dans le code source de phpmyadmin... après il reste toujours une marge d'erreur mais c'est mieux que de se baser uniquement sur remote_addr

- j'avais pas fait gaffe... j'ai rien dis...

- oui tu peut facilement modifier le user_agent mais encore faut il connaitre le user_agent de la personne que tu vise ce qui est faisable mais c'est une variable de plus... de toute façon si un gars réussis à récupérer le user_agent exacte (versioning & co) de sa cible il y a fort a parier qu'il aura récupéré l'ip aussi donc bon... c'est pas pour ce que ça change

concernant l'ip elle même maintenant... tu récupère le host via un classique gethostbyaddr() tu extrait l'ip qui est généralement renvoyée par le serveur BAS sur lequel est relié l'internaute du coup tu te retrouve avec le non du FAI et généralement avec le code du NRA (des fois même le n° de carte DSLAM et/ou les vp/vc DSLAM/BAS mais c'est plus rare faut pas trop compter dessus) que tu peux coupler avec les deux premiers blocks de l'ip qui sont propres au FAI le tout mixé via md5 avec, éventuellement, le user agent et là ton token reste fiable tout en évitant le plus possible les pb dû aux ip dynamique... c'est un poil moins fiable que l'ip au complet car certains FAI on une résolution ip => host un peu pauvre en infos (pas de nom de NRA par ex) mais ça reste, à mon sens, suffisamment fiable pour la plupart des utilisations... tu peux aussi te servir de uniqid() pour corser le tout...

uniqid(md5($uvars),true)

mais là ça devient de la parano je pense... .. . ;o)

@ tchaOo°
Utilisateur anonyme
29 juin 2009 à 18:01
Bjr Kankrelune,

Merci pour ton com, juste quelques remarques suite au tiennes :

- Le token est généré (md5) qu'une seule fois au chargement de la page, c'est un singleton.

- La récup de l'IP ne prend pas en compte les proxy, parcontre je ne vois pas d'autres moyens pour récupérer l'adresse IP de celui qui demande la page - si tu pouvais m'en dire plus ...

- Pour le check du token, je suis d'accord avec toi, pas besoin de vérifier s'il y a injection. C'est pour ça que la fonction check qui valide le token n'execute pas checkVars qui est une fonction indépendante. Je l'ai implémentée en tant que fonction utilitaire car sur certains formulaires les actions ne sont pas faites que par des personnes de confiance.

Concernant la remarque sur les FAI tu n'as pas tord, mais malgré tout le user_agent ne me plait pas car le client peut y mettre ce qu'il veut.
Pour le remote host, la variable n'est pas toujours renseignée, faut qu'apache soit configuré (mon code doit être générique et tourner sur un max de config). Concernant l'histoire d'ip et hostname si tu connais une lib d'autres fonctions, je suis vraiment preneur.

Bonne prog,
akh
kankrelune Messages postés 1293 Date d'inscription mardi 9 novembre 2004 Statut Membre Dernière intervention 21 mai 2015
29 juin 2009 à 17:08
ah au passage... stock ton token la première fois que tu le génère... inutile de faire 36 fois md5(... .. . ;o)

@ tchaOo°
kankrelune Messages postés 1293 Date d'inscription mardi 9 novembre 2004 Statut Membre Dernière intervention 21 mai 2015
29 juin 2009 à 17:05
A l'époque ou je travaillais sur le code de Xoops on avait intégrés les tokens dans les formulaire admin... l'idée ne date pas d'hier et c'est la meilleur solution, même la seule, pour lutter contre ce genre d'attaque... cependant l'utilisation de l'ip dans le token peut être problématique avec des utilisateur dont le FAI gère les ip en dynamique (on avait de gros pb avec AOL à l'époque) le mieux étant d'utiliser l'host de l'ip et non l'ip elle même... couplé par exemple avec le user_agent... beaucoup plus fiable... .. . ;o)

Au passage ta méthode pour récupérer l'ip est trop simpliste et peu fiable et à partir du moment ou tu check les tokens à chaque action tu n'a pas besoin de checker la présence de code executable dans les variables (ce qui pourrais poser pb dans un site comme code sources)

@ tchaOo°

@ tchaOo°
victorcoasne Messages postés 1101 Date d'inscription jeudi 24 avril 2003 Statut Membre Dernière intervention 23 juillet 2023 7
3 mars 2009 à 22:21
Ok merci des infos.
ça fait plaisir de voir que sur ce site y'a quand même des gens qui maîtrise bien leur sujet et qui donnent un avis objectif.
Sinon c'est une bonne source pour les gens qui n'aurait pas connaissance des principales failles existance, et avec tes explications ça en éclairera plus d'un.
Bonne prog,
@++
Utilisateur anonyme
3 mars 2009 à 10:52
Ok victor, t'as tout à fait raison quand tu parles de ton système propriétaire. Je reviens juste sur tes choix pour montrer leurs limitation et expliquer quelques fonctionnalités.

Le post n'est pas suffisant, en javascript tu peux faire du post avec de l'ajax ou même avec un formulaire dont le target est vers un iframe caché.

Le referer dont tu parles est un très bon choix :) ça limite les attaques à ton site seulement - mais elles peuvent subsister si sur ton site l'utilisateur peut y stocker des informations affichées. C'est le principe même des réseaux d'utilisateurs, ou forums. Si a ce moment l'utilisateur trouve une faille d'injection javascript il pourra te forcer à ton insu à executer des appels (post ou get).

C'est pour ça l'histoire de token : c'est que quand tu demandes la page de listing des utilisateurs que le token est créé et t'ouvre le droit d'en supprimer un.

Sans appeller cette page et trouver ce token, on ne pourra pas te forcer à executer la page.

Du moment ou tu as customisé ton site avec tes propres scripts tu court moins de problèmes côté administration mais le méchant hacker pourra s'amuser à créer des virus javascript si t'as des failles d'injections exploitables avec des failles CSRF, et là ton site n'est pas à l'abri de ce type d'attaque.
victorcoasne Messages postés 1101 Date d'inscription jeudi 24 avril 2003 Statut Membre Dernière intervention 23 juillet 2023 7
2 mars 2009 à 22:32
Je vois un peu mieux l'intérêt sauf que je n'utilise jamais de CMS et que je n'ai aucune page qui supprime tout (ça serait débile).
En plus tout mes deletes de BDD se font par POST et dans une page où seule moi est accès et seul moi sait comment elle fonctionne.
Je peux aussi dans ce cas teste le référer et n'autoriser que si c'est de mon site.
Utilisateur anonyme
2 mars 2009 à 13:49
Salut Victor,

En intégrant les vérifications normales tu laisses des failles.

On prends par exemple un espace d'admin sur ton site, sur lequel en exécutant le script delete_all.php, ton site est vidé. Moi étant méchant et sachant que tu utilises le fameux cms PhpDuke, je décide de te tendre un piège. En te connectant à ton panneau d'admin, tu décides après que je t'envoie une URL sur mon blog de consulter la page. Elle à un lien , or tu est loggué, donc tu as le droit de l'exécuter, sauf que tu ne l'as pas demandé.

Pareil, l'histoire de tickets, c'est pour te protéger de ce même type de faille, mais à l'intérieur de ton propre site, car tu peux avoir un forum et que je puisse y cacher une image vers cette url également.

C'est des failles encore mal comprises et peu exploitée, mais une grande majorité de sites sont vulnérables.

L'histoire du token est loin d'être inutile. L'idée est que tu crées une autorisation avant d'exécuter une action. Sans cette action au préalable, impossible de l'exécuter. Ca peut être piraté mais qu'avec des tonnes d'injection JS plus difficiles à coder, et qui souvent ne passent pas par injection en raison de la longeur de stockage des messages, ça sécurise donc à 99% des cas.
victorcoasne Messages postés 1101 Date d'inscription jeudi 24 avril 2003 Statut Membre Dernière intervention 23 juillet 2023 7
2 mars 2009 à 13:36
Salut,
Je penses que tu t'es inspiré de Kerberos car tu parles de tickets.
Tu sais qu'en intégrant à ta page les vérifications normales d'un espace membre on obtient le même effet.
En protégeant biensûr les requêtes avec addslashes() et les champs avec htmlentities() pour éviter que le navigateur interprête un script.
Bonne continuation et bonne prog,
@++
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
14 févr. 2009 à 00:00
Salut,

C'est marrant, ça me fait penser à un billet de Korben sur son blog récemment (il y a une petite dizaine de jours je crois) dans lequel il expliquait comment faire écrire ce qu'on veut sur le twitter de n'importe qui...
Il disait notamment qu'il n'était pas possible de se protéger contre cette "faille" : en fait, les protections à mettre en place doivent l'être sur le site, et pas par les visiteurs.
La meilleure protection consiste encore à ne pas garder ses sessions d'admins ouvertes et à personnaliser (autant que possible) les chemins des fichiers d'admin.
L'idée de token est bonne, moi, ça me plait.
J'ai pas regardé le code, mais... je te fais confiance ;)
Utilisateur anonyme
10 févr. 2009 à 20:31
Salut Warrior !

Le referer est utilisé pour vérifier la provenance de la requette, genre vérifier que l'appel ne se fait pas d'un webmail ou d'un autre host. Ca va un peu plus loin car ça vérifier si c'est pas une autre page que celle contenant le script qui serait appelée. Faudrait que je vérifie le cas d'une url rewritée mais le principe doit être le même.

Sinon la classe est configurable en écrivant ceci :
CrossSite::$checkHost = true / false;
CrossSite::$checkToken = true / false;

Concernant la vérification d'injection JS effectivement ça ne doit pas être case sensitive, parcontre la découpe de java\nscript est bien spécifique :) c'est un cas spécial.

Je refais un release, mais je suis certain que le filtrage des variables avant utilisation est indispensable vu le nombre de failles sur la majorité des sites.
winwarrior Messages postés 654 Date d'inscription jeudi 3 avril 2003 Statut Membre Dernière intervention 10 février 2009 1
10 févr. 2009 à 20:13
Salut,

J'ai lu la source en diagonale, mais n'y a t'il pas de problemes avec le referer en cas d'url rewriting?
Quelqu'un qui aurait un firewall qui bloque le header referer ne pourrait pas surfer sur le site?
Pour les méthodes escapeValue/checkValue, je doute qu'on puisse envisager toutes les possibilités.. là exemple idiot si je passe comme variable "<SCRIPT" (donc en majuscule), ou "jav\nascript" ça passe les filtres actuels, il y a tellement de manieres d'écrire ce genre de scripts..
Par contre l'idée des tokens est sympa :p

@+
Rejoignez-nous