Ce tuto est écris initialement pour PHP / MySQL, mais le principe peut très bien être appliqué pour d’autres langages et d’autres base de données.
Introduction
En terme de sécurité, il y a toujours différents risques à identifier
- risque de confidentialité : le vol de données
- risque de corruption : les données sont modifiées
- risque d’usurpation d’identité : un utilisateur se fait passer pour un autre (peut évoluer en risque juridique !!)
Il faut savoir que lorsque l’on utilise des solutions open_source, que ce soit des CMS (joomla, wordpress), des Framework (Symfony) ou des forums (phpBB), dés qu’une faille de sécurité est détectée sur l’un de ces outils, tous les sites les utilisant et n’ayant pas été mis à jours deviennent des cibles. Ne le prenez pas mal, en général ce n’est pas contre vous...
Les hackers utilisent des robots qui scannent les sites Internet et tentent d’utiliser toutes sortes de failles de sécurités connues.
Mais bon, quand on cherche, ces failles sont connues, et le moyen de les exploiter se trouve assez facilement sur le net.
De plus, lorsque vous n’êtes pas un pro du développement, les possibilités de créer des failles de sécurités sont très nombreuses, et il est très difficile de se prémunir efficacement d’une injection SQL par exemple :
- les données saisies dans un formulaire peuvent contenir des injections
- les données saisies dans un formulaire et ensuite affichée à l’écran peuvent contenir du code javascript malicieux
- les uploads de fichier peuvent permettre d’uploader des fichiers qui vont être exécuté sur le serveur
- les paramètres reçues dans l’URL peuvent déclencher des comportements inattendus
- etc...
Bref, partez toujours du principe que si quelqu’un veut attaquer votre site, il trouvera un moyen, et dans ce cas il faut faire en sorte qu’il fasse le moins de dégâts possible, et que sa marge de manœuvre soit la plus réduite possible.
Ici je vais vous parler d’un cas concret, le plus courant : nous allons partir du principe qu’un utilisateur mal intentionné à réussit à accéder à votre base de données (en général, c’est soit via de l’injection SQL, soit via un outil externe mal sécurisé, comme un phpMyAdmin).
Bon, notre ami est entré, et peux consulter la base de données, on va essayer de faire en sorte qu’il fasse le moins de mal possible à nos chers utilisateurs (certains ayant payé pour des bonus, légalement, ça pourrait donc nous coûter cher)
Etape 1, hacher le mot de passe
Ça semble évident, mais c’est souvent oublié, la première chose à faire est de hasher le mot de passe de l’utilisateur.
Le Hashage se distingue du cryptage par les règles suivantes :
Une fonction de hachage se doit donc d'être non déchiffrable (sens unique), pour tester la validité d’un mot de passe saisie, il faut alors le hacher à son tour et le comparer avec le résultat sauvegardé.
Pour commencer on peut utiliser la fonction SHA1 de MySQL, cette fonction permet un hachage de 160 bits, la fonction PASSWORD de MySQL peut être utilisée, mais elle est déconseillée par MySQL, elle ne devrait être utilisée uniquement en interne pour stocker les mots de passe de connexion des utilisateurs MySQL, pas de votre application (source : http://dev.mysql.com/doc/refman/5.0/fr/e...tions.html ).
Et pour vérifier le mot de passe :
Voilà, les mots de passes sont hachés et ne sont donc pas lisible par notre ami
Bon, mais notre ami peut toujours télécharger le mot de passe haché chez lui, et faire tourner un dictionnaire sur un ordinateur, jusqu’à trouver le mot de passe qui se hache de la même manière, il va ensuite pouvoir se connecter à votre application/jeu en utilisant le compte du joueur piraté.
Pour aller plus loin : vous pouvez par exemple regarder la fonction crypt (http://php.net/manual/fr/function.crypt.php ) en PHP qui (contrairement à ce que son nom indique) offre un hachage (et non un cryptage) assez plus poussé et surtout dépendant du système.
Etape 2, utiliser une graine
Pour éviter que l’utilisateur puisse retrouver le mot de passe "en force" comme vu précédemment, il suffit de le tromper, et de rajouter au mot de passe une graine qui sera hachée en même temps, la graine étant au niveau du code et non au niveau de la base de donnée notre ami n’en a pas connaissance.
Et pour vérifier le mot de passe :
Ainsi, si notre ami recherche le mot de passe chez lui, il trouvera la chaîne "MA_GRAINEmon_mot_de_passe"... Mais si votre graine contient des lettres, des chiffres, des majuscules et des caractères de ponctuation, il lui faudra environs 200 ans avant de trouver cette chaîne, ça vous laisse un peu de temps.
Pour aller plus loin : vous pouvez utiliser la clé en début et/ou en fin de chaîne, j’ai même vu une fois une graine qui était une fonction du login ce qui permet d’avoir une graine différente pour chaque utilisateur.
Etape 3, ajouter un hachage de l’utilisateur.
Bon, le mot de passe est haché, il est maintenant impossible à notre ami de casser le mot de passe des utilisateurs... oui, mais il y a toujours un mot de passe qu’il connaît : c’est le sien ! Notre ami a créé un compte sur votre jeu, il le retrouve dans la base de donnée, avec le résultat du hachage du mot de passe... il prend ce hach et le colle sur un autre utilisateur... Oh le vilain, il peut maintenant se connecter à la place de l’autre utilisateur en utilisant son propre mot de passe !!!
Si notre ami a accès à la base de donnée, ne chercher pas, il n’y a quasiment aucun moyen de l’empêcher de faire ça... mais par contre, on peut le détecter !
Rajoutons dans notre table un champ "HACH", ce champ va recevoir une chaîne de caractères permettant de vérifier que les données de notre utilisateur n’ont pas été modifiées par un moyen extérieur.
Maintenant à chaque fois que, dans notre programme on modifie l’utilisateur, on en profite pour mettre à jours ce hach :
Et lorsque l’on charge l’utilisateur, on contrôle que le hach est toujours valide :
Maintenant, si notre ami modifie le mot de passe, sans modifier le hach, le select va retourner is_ok à 0, signifiant que le mot de passe a été modifié sans mettre à jour le hach (ce qui aurait dût se passer si la modification avait eut lieu via l’application)
Vous me direz, oui, mais notre ami peut copier son hach comme il a copié son mot de passe vers le nouvel utilisateur... tout à fait, c’est pour ça qu’il ne faut pas prendre que le mot de passe, mais l’ensemble des données critiques :
- l’adresse mail (s’il modifie l’adresse mail, il n’a plus qu’à dire qu’il a perdu son mot de passe pour en recevoir un nouveau dans la boite mail de son choix)
- le login
- le profil utilisateur (tiens, et si je me mettais en administrateur pour pouvoir modifier tous les autres utilisateurs ?)
- les xps, points payant, etc… (allez, on va dire que j’ai dépensé 100 euros pour avoir des Cristaux en plus...)
Et pour sécuriser encore plus, on rajoute encore une graine, tant qu’on y est...
Maintenant à chaque fois que, dans notre programme on modifie l’utilisateur, on en profite pour mettre à jours ce hach :
Et lorsque l’on charge l’utilisateur, on contrôle que le hash est toujours valide :
Voilà, avec tout ça, le prochain qui voudra foutre la merde dans votre base utilisateur, il va en chier... et moins il y a de risques et plus vos utilisateurs seront content...
Introduction
En terme de sécurité, il y a toujours différents risques à identifier
- risque de confidentialité : le vol de données
- risque de corruption : les données sont modifiées
- risque d’usurpation d’identité : un utilisateur se fait passer pour un autre (peut évoluer en risque juridique !!)
Il faut savoir que lorsque l’on utilise des solutions open_source, que ce soit des CMS (joomla, wordpress), des Framework (Symfony) ou des forums (phpBB), dés qu’une faille de sécurité est détectée sur l’un de ces outils, tous les sites les utilisant et n’ayant pas été mis à jours deviennent des cibles. Ne le prenez pas mal, en général ce n’est pas contre vous...
Les hackers utilisent des robots qui scannent les sites Internet et tentent d’utiliser toutes sortes de failles de sécurités connues.
Mais bon, quand on cherche, ces failles sont connues, et le moyen de les exploiter se trouve assez facilement sur le net.
De plus, lorsque vous n’êtes pas un pro du développement, les possibilités de créer des failles de sécurités sont très nombreuses, et il est très difficile de se prémunir efficacement d’une injection SQL par exemple :
- les données saisies dans un formulaire peuvent contenir des injections
- les données saisies dans un formulaire et ensuite affichée à l’écran peuvent contenir du code javascript malicieux
- les uploads de fichier peuvent permettre d’uploader des fichiers qui vont être exécuté sur le serveur
- les paramètres reçues dans l’URL peuvent déclencher des comportements inattendus
- etc...
Bref, partez toujours du principe que si quelqu’un veut attaquer votre site, il trouvera un moyen, et dans ce cas il faut faire en sorte qu’il fasse le moins de dégâts possible, et que sa marge de manœuvre soit la plus réduite possible.
Ici je vais vous parler d’un cas concret, le plus courant : nous allons partir du principe qu’un utilisateur mal intentionné à réussit à accéder à votre base de données (en général, c’est soit via de l’injection SQL, soit via un outil externe mal sécurisé, comme un phpMyAdmin).
Bon, notre ami est entré, et peux consulter la base de données, on va essayer de faire en sorte qu’il fasse le moins de mal possible à nos chers utilisateurs (certains ayant payé pour des bonus, légalement, ça pourrait donc nous coûter cher)
Etape 1, hacher le mot de passe
Ça semble évident, mais c’est souvent oublié, la première chose à faire est de hasher le mot de passe de l’utilisateur.
Le Hashage se distingue du cryptage par les règles suivantes :
Citation :une fonction de hachage doit remplir quelques conditions de base :
- L'entrée peut être de dimension variable.
- La sortie doit être fixe.
- H(m) doit être relativement facile à calculer.
- H(m) doit être une fonction à sens unique.
- H(m) doit être "sans collision".
(Source : https://www.uqtr.ca/~delisle/Crypto/hachages/ )
Une fonction de hachage se doit donc d'être non déchiffrable (sens unique), pour tester la validité d’un mot de passe saisie, il faut alors le hacher à son tour et le comparer avec le résultat sauvegardé.
Pour commencer on peut utiliser la fonction SHA1 de MySQL, cette fonction permet un hachage de 160 bits, la fonction PASSWORD de MySQL peut être utilisée, mais elle est déconseillée par MySQL, elle ne devrait être utilisée uniquement en interne pour stocker les mots de passe de connexion des utilisateurs MySQL, pas de votre application (source : http://dev.mysql.com/doc/refman/5.0/fr/e...tions.html ).
UPDATE user SET mot_de_passe = SHA1('mon_mot_de_passe') ;
Et pour vérifier le mot de passe :
SELECT * FROM user WHERE mot_de_passe = SHA1('mot_de_passe_saisie');
Voilà, les mots de passes sont hachés et ne sont donc pas lisible par notre ami
Bon, mais notre ami peut toujours télécharger le mot de passe haché chez lui, et faire tourner un dictionnaire sur un ordinateur, jusqu’à trouver le mot de passe qui se hache de la même manière, il va ensuite pouvoir se connecter à votre application/jeu en utilisant le compte du joueur piraté.
Pour aller plus loin : vous pouvez par exemple regarder la fonction crypt (http://php.net/manual/fr/function.crypt.php ) en PHP qui (contrairement à ce que son nom indique) offre un hachage (et non un cryptage) assez plus poussé et surtout dépendant du système.
Etape 2, utiliser une graine
Pour éviter que l’utilisateur puisse retrouver le mot de passe "en force" comme vu précédemment, il suffit de le tromper, et de rajouter au mot de passe une graine qui sera hachée en même temps, la graine étant au niveau du code et non au niveau de la base de donnée notre ami n’en a pas connaissance.
UPDATE user SET mot_de_passe = SHA1(
CONCAT('MA_GRAINE', 'mon_mot_de_passe')
) ;
Et pour vérifier le mot de passe :
SELECT * FROM user WHERE mot_de_passe = SHA1(
CONCAT('MA_GRAINE', 'mot_de_passe_saisie')
);
Ainsi, si notre ami recherche le mot de passe chez lui, il trouvera la chaîne "MA_GRAINEmon_mot_de_passe"... Mais si votre graine contient des lettres, des chiffres, des majuscules et des caractères de ponctuation, il lui faudra environs 200 ans avant de trouver cette chaîne, ça vous laisse un peu de temps.
Pour aller plus loin : vous pouvez utiliser la clé en début et/ou en fin de chaîne, j’ai même vu une fois une graine qui était une fonction du login ce qui permet d’avoir une graine différente pour chaque utilisateur.
Etape 3, ajouter un hachage de l’utilisateur.
Bon, le mot de passe est haché, il est maintenant impossible à notre ami de casser le mot de passe des utilisateurs... oui, mais il y a toujours un mot de passe qu’il connaît : c’est le sien ! Notre ami a créé un compte sur votre jeu, il le retrouve dans la base de donnée, avec le résultat du hachage du mot de passe... il prend ce hach et le colle sur un autre utilisateur... Oh le vilain, il peut maintenant se connecter à la place de l’autre utilisateur en utilisant son propre mot de passe !!!
Si notre ami a accès à la base de donnée, ne chercher pas, il n’y a quasiment aucun moyen de l’empêcher de faire ça... mais par contre, on peut le détecter !
Rajoutons dans notre table un champ "HACH", ce champ va recevoir une chaîne de caractères permettant de vérifier que les données de notre utilisateur n’ont pas été modifiées par un moyen extérieur.
Maintenant à chaque fois que, dans notre programme on modifie l’utilisateur, on en profite pour mettre à jours ce hach :
UPDATE user SET hach = MD5(mot_de_passe);
Et lorsque l’on charge l’utilisateur, on contrôle que le hach est toujours valide :
SELECT user.*, IF(MD5(mot_de_passe) = hach, 1, 0) AS is_ok FROM user;
Maintenant, si notre ami modifie le mot de passe, sans modifier le hach, le select va retourner is_ok à 0, signifiant que le mot de passe a été modifié sans mettre à jour le hach (ce qui aurait dût se passer si la modification avait eut lieu via l’application)
Vous me direz, oui, mais notre ami peut copier son hach comme il a copié son mot de passe vers le nouvel utilisateur... tout à fait, c’est pour ça qu’il ne faut pas prendre que le mot de passe, mais l’ensemble des données critiques :
- l’adresse mail (s’il modifie l’adresse mail, il n’a plus qu’à dire qu’il a perdu son mot de passe pour en recevoir un nouveau dans la boite mail de son choix)
- le login
- le profil utilisateur (tiens, et si je me mettais en administrateur pour pouvoir modifier tous les autres utilisateurs ?)
- les xps, points payant, etc… (allez, on va dire que j’ai dépensé 100 euros pour avoir des Cristaux en plus...)
Et pour sécuriser encore plus, on rajoute encore une graine, tant qu’on y est...
Maintenant à chaque fois que, dans notre programme on modifie l’utilisateur, on en profite pour mettre à jours ce hach :
UPDATE user SET hach = MD5(CONCAT(
mot_de_passe, login, 'MA GRAINE', email, id_profil, xp, 'UNE AUTRE GRAINE', nb_christaux
));
Et lorsque l’on charge l’utilisateur, on contrôle que le hash est toujours valide :
SELECT user.*, IF(MD5(CONCAT(
mot_de_passe, login, 'MA GRAINE', email, id_profil, xp, 'UNE AUTRE GRAINE', nb_christaux
)) = hach, 1, 0) AS is_ok
FROM user ;
Voilà, avec tout ça, le prochain qui voudra foutre la merde dans votre base utilisateur, il va en chier... et moins il y a de risques et plus vos utilisateurs seront content...