Sécuriser sa table utilisateur
#1
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 :
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...
Répondre
#2
pas mal du tout... très joli tuto! J'aurais mis "tutoriel moyen", parce qu'au final, ça reste relativement simple a mettre en oeuvre.

On aurait pu crypter le login de la même manière que le mot de passe (en général, le login/pseudo n'est pas utilisé pour autre chose que l'authentification)
Je signale que je ne détiens pas la vérité unique et absolue, je peux me tromper. La critique peut aussi être constructive. Critiquez moi!
La quête d'Ewilan

http://easy2hack.ma-soiree.be
Répondre
#3
Sans oublié de ne pas mettre un mot de passe comme root etc, pour les parties des administrateurs que ce soit dans l'application ou encore au niveau de la base de donnée.
Cela coule de source mais le nombre de personne qui utilise de tel mot de passe ...
Concernant les mots de passe sensibles, utiliser les minuscules, majuscules, caractère spéciaux et des chiffres,
un mot de passe dépassant les 12 caractères est l'idéal, d'autres part en changer régulièrement au moins tout les 2 mois voir plus souvent.
Répondre
#4
@NicoMSEvent : Pas faux, j'ai corrigé...
C'est pas bien compliqué à mettre en place en effet, mais c'est pour répondre à un besoin relativement avancé... Comme dirait certain c'est un problème de riche
Répondre
#5
Très bon tutoriel...

Merci...
Répondre
#6
Très bien !

Je rajouterais des trucs de base du genre interdire les actions drop, grant à la connection SQL et interdire delete sur toutes les tables où logiquement il n'y a pas de suppression à faire.
Répondre
#7
Merci pour vos avis.

Attention par contre de ne pas mélanger, ce tuto concerne uniquement la sécurité de la table et de sa structure, et de proposer des idées à reprendre pour toutes données sensibles dans la base de données. Concernant la gestion des droits, du compte utilisateur MySQL (et non de l'application), c'est de la configuration du serveur, pour moi c'est un autre sujet (qui mériterait sans doute un tuto, je vous l'accorde).

@NicoMSEvent : je n'avais pas fait attention à ta remarque sur le cryptage du login... je trouve que ça n'apporte rien de plus en terme de sécurité et par contre plusieurs inconvénient : c'est sur le login que l'on vérifie l'unicité en général, et si le nom du joueur/perso peut être modifiable, dans les modules d'administration, avoir un login unique, figé et en clair est plus pratique (pour identifier les tricheries)... Et puis si un utilisateur t'envoie un mail "je n'arrive plus à me connecter, mon login c'est machin..."
Répondre
#8
(12-22-2010, 05:28 PM)Jeckel a écrit : Attention par contre de ne pas mélanger, ce tuto concerne uniquement la sécurité de la table et de sa structure, et de proposer des idées à reprendre pour toutes données sensibles dans la base de données. Concernant la gestion des droits, du compte utilisateur MySQL (et non de l'application), c'est de la configuration du serveur, pour moi c'est un autre sujet (qui mériterait sans doute un tuto, je vous l'accorde).
Un utilisateur bridé (attention, je ne parle pas de chinois :p) qui n'aurait que les droits minimums pour exécuter les scripts (pas de UPDATE, ni de DELETE sur les tables qui n'en on pas besoin, ni de SHOW TABLES, et autres commandes "système"... )
Donc, ne pas utiliser l'utilisateur "root" pour accéder a la base de donnée, devrait suffire a ce niveau

(12-22-2010, 05:28 PM)Jeckel a écrit : @NicoMSEvent : je n'avais pas fait attention à ta remarque sur le cryptage du login... je trouve que ça n'apporte rien de plus en terme de sécurité et par contre plusieurs inconvénient : c'est sur le login que l'on vérifie l'unicité en général, et si le nom du joueur/perso peut être modifiable, dans les modules d'administration, avoir un login unique, figé et en clair est plus pratique (pour identifier les tricheries)...

Un login n'est pas forcément l'e-mail ou le nom du personnage. J'ai d'ailleurs envie (dans mon jeu) de pouvoir controler plusieurs personnage avec un même compte 2

Le login est la moitié des données d'identification, et le mot de passe la seconde moitié. En cryptant le login, ou protège la premiere moitié 2
Si ton avis diverge, je serais heureux que tu argumentes pour me donner un angle de vision différent 2

(12-22-2010, 05:28 PM)Jeckel a écrit : Et puis si un utilisateur t'envoie un mail "je n'arrive plus à me connecter, mon login c'est machin..."
C'est un faux problème, si tu fais un SELECT * FROM joueurs WHERE login=PASSWORD('utilisateur_perdu') , tu devrais arriver a le retrouver 16
Maintenant, si il a perdu son login, c'est plus embetant... il pourrait en creer un nouveau comme pour le mot de passe sur base de son e-mail (avec e-mail de confirmation evidemment, éventuellement en lui envoyant en clair son login, mais pas son mot de passe -> j'ai pour politique de ne jamais afficher en clair les mots de passe)
Je signale que je ne détiens pas la vérité unique et absolue, je peux me tromper. La critique peut aussi être constructive. Critiquez moi!
La quête d'Ewilan

http://easy2hack.ma-soiree.be
Répondre
#9
HASHAGE BORDEL.... PAS CRYPAGE...
Répondre
#10
Révision du tuto :
- remplacement du "cryptage" par du "hachage"
- remplacement de la fonction PASSWORD (que je viens de voir comme déconseillée par MySQL) par un SHA1 (sur 160 bits).
- ajout d'un peu plus de documentation et de références

Le SHA1 est considéré comme plus sûr que le MD5, d'où l'utilisation du SHA1 pour le chiffrement du mot de passe, et du MD5 pour le hach de contrôle.
(12-23-2010, 09:39 AM)NicoMSEvent a écrit : Un login n'est pas forcément l'e-mail ou le nom du personnage. J'ai d'ailleurs envie (dans mon jeu) de pouvoir controler plusieurs personnage avec un même compte 2

Le login est la moitié des données d'identification, et le mot de passe la seconde moitié. En cryptant le login, ou protège la premiere moitié 2
Si ton avis diverge, je serais heureux que tu argumentes pour me donner un angle de vision différent 2

Si tu as plusieurs personnages pour un même compte (c'est mon cas aussi) tu as sans doute une interface d'administration des personnages, et une interface d'administration des utilisateurs ? (en tout cas c'est mon cas, en particulier pour pouvoir gérer, plus tard, les points payant qui seront du niveau utilisateur et non personnage)

Dans ce cas, dans l'écran d'administration des utilisateurs (et non des personnages) j'ai besoin d'un moyen de pouvoir identifier clairement un utilisateur... je pourrais le faire sur l'adresse mail, mais je préfère utiliser le login.

En plus, je trouve que le chiffrement du login n'apporte pas vraiment de sécurité en plus...

Sauf... pour les utilisateurs qui vont utiliser leur login comme mot de passe... mais dans ce cas, j'ai tendance plutôt à rajouter un contrôle à la création du compte pour imposer un niveau de sécurité minimum sur le mot de passe (minuscule + majuscule + au moins un caractère non alphabétique et différent du login)

Voilà, maintenant si tu as un autre champs, unique, dans ta table utilisateur qui te permet de retrouver facilement ton utilisateur et savoir de qui tu causes dans ton interface d'administration, alors je ne vois pas vraiment d'inconvénient à chiffrer aussi le login.
Répondre




Utilisateur(s) parcourant ce sujet : 1 visiteur(s)