Sécurité : Protégez vous contre le vol de sessions

Lorsque la faille concernant le vole de session n’est pas bouchée, il possible qu’un pirate vol votre session d’administrateur puis détruise complètement votre site. Et pourtant il est assez facile de protéger votre site contre cette faille. C’est ce que je vais vous expliquer dans cet article.

D’où vient cette faille

Tout d’abord, regardons comment un pirate peut voler une session. Ensuite, nous pourrons mieux nous défendre.

Les sessions ont étés inventées pour permettre à un site de garder des informations (login, nom, prénom, etc.) de page en page alors que le protocole HTTP ne le permettait pas. L’idée est de donner un numéro unique (par exemple : ffend6skaaaht4igini1h7g732) au navigateur client lorsqu’il demande la première page. Puis, quand le visiteur va accéder à une seconde page le navigateur donnera au serveur son numéro de session.

Ce numéro est transmit via cookies. Il est le seul moyen de différencier deux sessions. Il suffit donc à un pirate d’intercepter vos cookies pour voler une session.

Voici un petit schéma pour mieux comprendre les transactions entre le navigateur et le serveur :

Dialogue navigateur/serveur et session

Dialogue navigateur/serveur et session

1 – Le navigateur, qui n’a pas encore de session, demande une page en informant le serveur qu’il n’a pas de session ouverte.
2 – Le serveur crée la session et la stock. Puis envoi la page demandé en indiquant le numéro de session.
3 – Le navigateur qui a mémorisé son numéro de session demande une autre page et donne son numéro de session au serveur.
4 – Le serveur peut aller lire la session dans ses données et peut donc créer la page adaptée à la demande du navigateur.

Seulement voilà, un pirate vient s’introduire dans le schéma ci-dessous :

Dialogue navigateur/serveur/pirate – session

Dialogue navigateur/serveur/pirate – session

Dans ce schéma on comprends assez facilement que le pirate (ici joué par un utilisateur Internet Explorer) a utilisé le numéro de session du gentils FireFox (non non, je ne hais pas IE ^^).

Le problème se pose en fait du coté Apache et PHP qui ne se basent que sur cet identifiant de session pour définir quelle est la session de l’utilisateur.

Comment se protéger

Heureusement pour nous, il existe une solution pour boucher cette faille de sécurité.

En se posant la question « Que différencie le bon du mauvais client ? », j’ai pu établir une petite liste :

  1. Le navigateur utilisé
  2. La version du navigateur
  3. La liste des mime-types acceptés par le navigateur
  4. La langue du navigateur
  5. L’encodage de caractères
  6. La compression de fichiers
  7. L’adresse IP
  8. Le système d’exploitation

Dans cette liste j’ai gardé uniquement les valeurs qu’il est possible de contrôler en PHP.

  1. On peut regrouper les points 1, 2 et 8 car ces trois valeurs se trouvent dans la variable $_SERVER[‘HTTP_USER_AGENT’].
  2. La liste des mime-types acceptés : $_SERVER[‘HTTP_ACCEPT’].
  3. La langue : $_SERVER[‘HTTP_ACCEPT_LANGUAGE’].
  4. L’encodage de caractère : $_SERVER[‘HTTP_ACCEPT_CHARSET’].
  5. La compression des fichiers : $_SERVER[‘HTTP_ACCEPT_ENCODING’].
  6. L’adresse IP s’obitient avec la fonction ci-dessous:
function IP(){
	if( !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ){
		if( strchr($_SERVER['HTTP_X_FORWARDED_FOR'],',') ){
			$tab=explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
			$realip=$tab[0];
		}else{
			$realip=$_SERVER['HTTP_X_FORWARDED_FOR'];
		}
	}else{
		$realip=$_SERVER['REMOTE_ADDR'];
	}
	return $realip;
}

Mise en place

Pour me simplifier la vie, et pour simplifier la votre au passage, j’ai créer une petit classe statique qui me gère la sécurité de la session toute seule. Vous pouvez la télécharger. (Je signal au passage que vous pouvez faire ce que vous voulez de ce code)

Pour l’utiliser c’est simple, remplacez votre ancien code :

session_start();

par ma classe :

include_once 'OPSession.inc.php';
OPSession::Start();

Et voilà le tour est joué, vos sessions sont protégées contre le vole. La seule solution pour qu’un voleur de session puisse encore réussir son attaque c’est de connaitre les 8 valeurs données en début du paragraphe « Comment se protéger ».

Si vous avez des idées de compléments, n’hésitez surtout pas à m’en informer, afin que notre communauté puisse être au top de la sécurité. Merci d’avance à tous les futures contributeurs.

Tagués avec : , , , , , , , , ,
Publié dans Sécurité
11 commentaires pour “Sécurité : Protégez vous contre le vol de sessions
  1. Web codeur dit :

    Bonjour,

    Félicitations pour cet excellent article sur un point de sécurité qui est souvent mal compris et donc négligé.

    Il faut aussi penser qu’il y a le vol d’IP aussi. Mais la le niveau du pirate n’est plus vraiment celui d’un débutant.

    Par contre, depuis le version 5.X.X (je ne sais plus laquel précisément), la fonction session_regenerate_id(), peut prendre comme paramètre une option qui permet de supprimer les anciens id de session.

    Malheureusement celui est à false par défaut. Or afin de mieux sécuriser il convient de le mettre a true.

    Une autre chose afin de complèter ce script, si je peux me permettre, se serait de comparer via un cookie l’id de session de la page précédent avec celui de la nouvelle page.

    Si les deux sont identiques, c’est que l’utilisateur est le même, dans ce cas on régénère un nouvel id de session (en prenant soin de supprimer le précédent comme je l’ai dit plus tôt) et on renvoie cet id dans un cookie afin de retenter le même teste sur la page suivante et ainsi de suite.

    Je pense que cette solution offre une meilleure résistance au piratage de données.

    En tout cas bonne continuation.
    Bien cordialement.

  2. Olivier PEREZ dit :

    Bonjour,
    En effet, s’il y a vol d’IP cette protection est moins efficace. Cependant, comme tu le rappelle, le vol d’IP requiert un niveau plus élevé de piratage.

    En ce qui concerne l’utilisation de cookies entre 2 pages, je pense que ce ne serait pas réellement une sécurité en plus.
    Car le vol de session se fait en volant les cookies. Donc quand le pirate volera la session, il volera aussi les cookies de la victime et cette vérification ne sera pas effective.

    Ou peut-être que je n’ai pas bien saisi la solution que tu proposes.

  3. Web codeur dit :

    Oui c’est sûr, en fait je me sers de la session en complément des informations que vous avez décrites précédemment.

    J’utilise la session sur le même principe que les jetons (vous avez d’ailleurs rédigé un article dessus, il me semble), puisque la génération automatique des sessions utilise des clés avec des codages plus complexes.

    Après pour ce qui est du vol de cookie, c’est vrai. Je pense qu’utiliser la fonction crypt() de PHP peut être une bonne idée puisqu’en théorie cette fonction est indescriptible .

    Qu’en pensez-vous ?

    Bien cordialement.

  4. Olivier PEREZ dit :

    J’ai fait quelques recherches et je m’aperçois que très peu de développeurs utilisent la fonction crypt(). En général, on entend plus parler de md5() ou sha1() qui sont eux aussi des fonctions de hashage.
    Pour ce qui est du choix entre le MD5 et le SHA1, les avis penchent plutôt vers le SHA1 pour 2 raisons majeurs :

    Le SHA1 est une méthode de hashage sur 40 octets alors que le MD5 n’est que sur 32
    Il existe de nombreux outils pour casser (et non déchiffrer puisque c’est impossible) un MD5 alors qu’il en existe peu pour le SHA1

    Personnellement j’utilisais le MD5 jusqu’à avoir découvert le SHA1. Maintenant j’utilise un combo des 2 ^^

  5. Web codeur dit :

    Oui en effet, pour tout ce qui est mot de passe, j’utilise le MD5. Après pour ce qui est de la fonction SHA1, je ne m’étais pas encore penché dessus. Et à ce que je vois, c’est pour ainsi dire, une fonction MD5 améliorée.

    J’ai continué mes recherches sur la sécurité des sessions, puisque je suis dans la réalisation d’un CMS Open Source basé sur la librairie de Zend Framework. Et malgré toutes mes tentatives, je pense qu’il est très difficile, voir même impossible de sécuriser des sessions à 100%.

    Bon il faut aussi prendre en compte que les personnes qui « s’amusent » écouter le trafic ou hacker un compte ont un niveau vraiment avancé.

    Du coup, j’ai fini par pendre l’ip de la personne ainsi que l’user agent que j’ai associé de la façon suivante :

    $userKey = md5($userAgent . '-' . $ip);

    Et je stocke dans une base de données cette clé avec d’autres informations. J’ai d’ailleurs appris qu’il était intéressant de créer un nombre limite de jetons. C’est-à-dire par exemple, 20 jetons maximum par IP sur des fenêtres de cinq minutes.

    Après, je ne l’ai pas encore mis en pratique et je ne sais pas encore ce que ça peut donner. En effet, cela voudrait dire qu’il faille déconnecter l’utilisateur toutes les une heure et demie.

    c’est ennuyeux pour un administrateur qui compose un article ou fait une opération qui nécessite du temps.

    Bon je m’arrête ici, car je vais finir par saturer votre base de données à écrire des romans LOL.

    Bien cordialement.

  6. Olivier PEREZ dit :

    A partir du moment où des données viennent du visiteur (et même à partir du moment où un système a été pensé par un humain), il devient difficile voire même impossible de mettre en place une sécurité fiable à 100%.
    Maintenant, on peut poser un maximum de protections pour limiter le plus possible les actions d’un pirate et espérer ainsi qu’il abandonne avant d’avoir trouvé une faille.

    Pour ce qui est des jetons, je penses que 20 jetons est un nombre pas mal car on ouvre rarement 20 pages en parallèles (en partant du principe d’un nouveau jeton par page).

    En ce qui concerne la déconnexion de l’utilisateur ça peut être intéressant de le déconnecter au bon d’une certaine durée d’inactivité, par exemple : si l’utilisateur ne fait rien pendant 20 minutes on supprime sa session. On peux se dire que ça va poser problème si le visiteur ne touche pas la page pendant ces 20 minutes mais on peut rafraichir la session (par AJAX ou en forçant le rechargement d’une page) toutes les 15 minutes.
    Avec cette déconnexion, si un utilisateur quitte son navigateur sans se déconnecter, le pirate n’aura que 20 minutes pour agir.
    On peut bien sûr réduire ce temps pour optimiser la sécurité mais il faut bien faire attention à ne pas surcharger le serveur.

    J’ai bien l’impression qu’avec toutes ces idées on arrive à un bon niveau de sécurité. Ça commence à devenir intéressant.

  7. Olivier dit :

    Merci beaucoup.
    Celà signifie t il qu’il « suffit » de se servir de cette classe pour que le système de session soit entièrement sécurisé ?
    Si non, à quoi faut il veiller ?

    Merci

  8. Olivier PEREZ dit :

    En clair, pour sécuriser l’utilisation de sessions dans ton site, tu n’as qu’à inclure le fichier « OPSession.inc.php » et remplacer tes appels à « session_start() » par « OPSession::Start() ».

    Sinon pour sécuriser d’autres choses je ne peux pas te faire de liste exhaustive. Il n’y a pas de limite aux nombre de failles.
    Les failles les plus connus sont :
    – les injections SQL : http://www.experience-developpement.fr/php-pdo-requetes-preparees/
    – les CSRF : http://www.experience-developpement.fr/jouez-le-jeton-de-la-securite/
    – les XSS : Je n’ai pas encore fait d’article là dessus mais ça arrivera un jour

    Le tout, en sécurité ce de toujours se dire que : Ce qui vient de l’utilisateur est TOUJOURS pirate. C’est en partant de ce principe qu’on fait les meilleurs protections.

  9. BabyGeek dit :

    Bonjour,
    Très bon article !

    Le risque est que le pirate découvre la combinaison formant la clé de vérification, ce qui est encore plus facile si le pirate a un accès de l’intérieur au site. Il pourra se créer une table arc-en-ciel en cryptant dans plusieurs ordres ses propres données (user agent, ip…) jusqu’à trouver le hash correspondant avec la valeur de son cookie.

    C’est pourquoi je rajouterais un sel aléatoire à la clé de vérification, sel qui serait ensuite stocké dans la base de donnée. Et puis pourquoi pas un ordre aléatoire pour les différentes valeures composant la clé de vérification, ordre qui sera également stocké dans une base de donnée.

    Le sel n’empêche en rien le pirate d’y aller à l’instinct en essayant de se connecter avec différents user-agents etc mais il empêche au moins l’attaque de la combinaison en elle même.

  10. Olivier PEREZ dit :

    A mon avis, quand le pirate à accès à l’intérieur du site, il n’a plus vraiment besoin de trouver les clés en questions…

  11. Glowes dit :

    Sans vouloir ruiner vos efforts ces mesures ne servent a rien, en effet pour récupérer un id de session et créer le cookie qui va avec il y a deux solutions:
    Sniffer tout ce qui passe sur le port 80 entre le serveur et le client légitime et a ce moment là pourquoi s’embêter à voler l’ID puisqu’on a le login et le mot de passe (les données post des formulaires transitent en clair avec la requête HTTP).

    Ou bien avoir directement accès au pc de la victime et dans ce cas précis toutes les informations censées identifier l’utilisateur légitime seront connues du pirate et aisément falsifiables puisqu’une requête HTTP c’est juste un morceau de texte qu’on écrit sur un socket pour info je me suis codé il y a quelques temps un petit programme me permettant d’envoyer en rafale des requêtes HTTP custom, on peut faire la même chose avec telnet il doit même exister des addons pour firefox qui font ça.

    Bien, admettons que le pirate n’ait pas commencé a sniffer la connexion au moment où l’utilisateur légitime se connectait, ce qui peut arriver, encore une fois toutes les autres informations sont récupérables par le pirate, donc absolument pas secrètes.

    En plus si c’est entre collègues eh bien ces informations sont aussi connues vu qu’en entreprise c’est un os, un navigateur etc pour tout le monde, quand à l’ip j’en parle même pas c’est pareil même pas besoin de s’embêter à la spoofer.

    Ça vaut aussi pour le cas du gentil voisin qui squatte votre wifi.

    Bref en résumé se baser sur des données transmises par un client pour authentifier l’utilisateur c’est tout ce qu’il y a de moins fiable, et une perte de temps, il faut tout simplement accepter le fait que tant qu’il n’existera pas de système d’authentification valable cad qui ne se base pas sur la bonne foi de la personne qui transmet les données on aura beau « sécuriser » tout ce qu’on veut au niveau de nos apps ça changera rien du tout.

    Il faut accepter ce risque et agir en conséquence, avec des parades, pas fiables a 100% mais l’une des meilleures consiste a donner du fil à retordre au pirate, a lui compliquer la tâche, plus il mettra de temps à réussir à s’infiltrer moins il aura de chances d’y arriver.

    Pour les sessions regénérer un ID a chaque fois que la page est rafraichie est une bonne idée, ça élimine déjà les 3/4 des petits pirates en herbe car quand l’utilisateur légitime aura rafraichi la page l’ID dont le pirate disposera ne sera plus bon il n’aura eu que très peu de temps pour agir, a supposer qu’il n’ait eu qu’une seule occasion de voler l’ID (cas de la fouille sur pc en l’absence de l’utilisateur).

    Ça n’élimine pas les attaques par sniffing, et là il faut mettre en place l’utilisation du ssl qui a mon sens devrait être généralisée peu importe la sensibilité de l’application m’enfin.

    Mettre en place une procédure d’urgence, un bouton rouge dans le backend (comme celui pour la bombe nucléaire) a cliquer en cas d’infiltration qui ferme et efface directement toutes les sessions, qui désactive tous les comptes utilisateurs (sauf le votre bien sur) en leur envoyant un email contenant un lien pour rentrer un nouveau mot de passe et les empêchant de se loguer tant qu’ils n’auront pas enregistré un mot de passe différent de l’ancien, qui change également le mot de passe d’accès à la base de données.

    Enfin un mot sur les mots de passe, le md5 et le sha1 ne sont plus reconnus comme étant viables depuis plusieurs années bientôt, le sha512 et blowfish sont des alternatives efficaces.

    L’utilisation des salts pour empêcher les failles dûs aux gugusses qui prennent 123456 comme mot de passe est bien entendu indispensable.

    Du reste il n’y a qu’a croiser les doigts et être suffisamment réactif.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*