OVH Cloud OVH Cloud

Systeme d'authentification.

8 réponses
Avatar
Jean-Pierre
Bonjour,

Je suis en train de conçevoir un système d'authentification
d'utilisateur en PHP, sans prétentions. Le système a pour but
d'identifier un nom d'utilisateur et un mot de passe stockés dans une
base de données MySQL.

Le tout est programmée en orienté objet (modèle PHP5). J'ai
implémenté la plus part des méthodes de cette classe que j'ai nommé
"identification" (pour la posterité :)).

Seulement, je cherche à optimiser "au mieux" le code. Pour identifier
un utilisateur, j'ai pensé à deux moyens fondamentalement
différents.

Le premier consiste a obtenir un résultat MySQL par la requête :

SELECT * FROM table_identification WHERE login='pseudonyme' AND
password ='mot_de_passe' LIMIT 0, 1

En effectuant cette requête (et en l'ayant protégée des injections
SQL...), il est possible d'obtenir un résultat (VRAI si il y une
correpsondance et sinon rien).

Que pensez-vous de ce système ? est-il trop "binaire" ou dangereux ?

Le second système, que j'ai vu dans la plus part des scripts PHP que
j'ai consulté, consiste à aller chercher le mot de passe dans la base
et le comparer directement avec la saisie utilisateur, ça me parait
plus exposé comme solution, mais es-ce le meilleur moyen ?

Qu'en pensez-vous ?

Merci d'avance

8 réponses

Avatar
John GALLET
Bonjour,

Le tout est programmée en orienté objet


Magnifique. Pour accepter un login/pass en entrée et renvoyer un boolén
ou au mieux un rang de table en sortie, le débutant moyen va faire "de
l'objet". Z'êtes pénibles, hein, les promoteurs à tout crins. Bref.

SELECT * FROM table_identification WHERE login='pseudonyme' AND
password ='mot_de_passe' LIMIT 0, 1


Comme toujours, la vraie question à se poser c'est "quel est le besoin
?".

Est-ce que les données sélectionnées seront utilisées plus loin dans
l'application ?
Si oui, alors c'est la méthode à employer. On fait de préférence pas
SELECT * mais explicitement le nom des champs, mais c'est la bonne
méthode. Attention au LIMIT qui n'est absolument pas portable.

Si non, alors on a deux solutions logiques, et pas trois.
- ou on en profite pour mettre à jour l'heure de login en faisant un
UPDATE loosers SET last_conn=NOW() WHERE login='...' AND pass='...' et
on teste mysql_affected_rows()==1 (OK) else KO.
- ou on s'en fout et on fait un SELECT COUNT(login) FROM [la même
chose], un $totoþtch_row, et ou $toto[0]==1 (ok) ou KO.

En effectuant cette requête (et en l'ayant protégée des injections
SQL...),
Et comment...


Que pensez-vous de ce système ? est-il trop "binaire" ou dangereux ?
Il est au pire sous-performant car faisant transiter tout le rang de la

table alors que ça sert à rien, et surtout dans ce cas illogique, ou
parfaitement approprié si on doit de toutes façons récupérer les données
de la table (gestion de droits par exemple).

Le second système, que j'ai vu dans la plus part des scripts PHP que
j'ai consulté, consiste à aller chercher le mot de passe dans la base
et le comparer directement avec la saisie utilisateur, ça me parait
plus exposé comme solution, mais es-ce le meilleur moyen ?


Pas plus exposé, mais complètement débile dans tous les cas. Les gens
qui écrivent ça n'ont rien compris à l'utilité d'un SGBD. Si on a des
clauses WHERE c'est pas pour faire joli.

a++;
JG

Avatar
CrazyCat
Jean-Pierre wrote:
Le premier consiste a obtenir un résultat MySQL par la requête :
SELECT * FROM table_identification WHERE login='pseudonyme' AND
password ='mot_de_passe' LIMIT 0, 1
Le second système, que j'ai vu dans la plus part des scripts PHP que
j'ai consulté, consiste à aller chercher le mot de passe dans la base
et le comparer directement avec la saisie utilisateur, ça me parait
plus exposé comme solution, mais es-ce le meilleur moyen ?


Si tu veux mon avis:
1) évites les mots de passe en clair, utilises un MD5.
2) ton select actuel peut provoquer des erreurs selon ton traitement suivant
3) le second système est, amha, horrible et périlleux.

voici ce que j'utilise très souvent:
<?
$pseudonyme = strip_code($_POST['login']);
// strip_code est une fonction qui retire tout ce qui pourrait etre
dangereux
$password = md5($_POST['password']);
$sql_user = "SELECT COUNT(*) AS id FROM table_identification WHERE
login='$pseudonyme' AND password='$password'";
$req_user = mysql_query($sql_user);
list($nb_id) = mysql_fetch_rows($req_user);
if ($nb_id > 0) {
// utilisateur valide
} else {
// erreur
}
?>

Utiliser un COUNT(*) permet de toujours avoir un résultat valide, alors
que dans ta première requète, en cas d'erreur, tu dois gérer une erreur
(logique) ce qui me semble périlleux si tu n'as pas la maitrise du
serveur (et des warning).

--
Découvrez Original War: http://www.original-war.org
Humour: http://www.chatfou.com
Tchattez en liberté: http://www.crazy-irc.net

Avatar
Guillaume Bouchard
Jean-Pierre wrote:
Bonjour,


Bonjour.

Je suis en train de conçevoir un système d'authentification


On est tous un jour plus ou moins passé par là.

j'ai pensé à deux moyens fondamentalement
différents.

Le premier consiste a obtenir un résultat MySQL par la requête :

SELECT * FROM table_identification WHERE login='pseudonyme' AND
password ='mot_de_passe' LIMIT 0, 1


Je dirais que tu peux te passer du LIMIT 0,1. Comme tu as surement un
index de type unique sur ta colone login, Mysql s'arretera a la première
ligne rencontrée.

En effectuant cette requête (et en l'ayant protégée des injections
SQL...), il est possible d'obtenir un résultat (VRAI si il y une
correpsondance et sinon rien).


En effet.

Que pensez-vous de ce système ? est-il trop "binaire" ou dangereux ?


Non, je ne vois pas où est le danger.

Le second système, que j'ai vu dans la plus part des scripts PHP que
j'ai consulté, consiste à aller chercher le mot de passe dans la base
et le comparer directement avec la saisie utilisateur, ça me parait
plus exposé comme solution, mais es-ce le meilleur moyen ?


Tu veux dire :

SELECT password ... WHERE login = 'truc'
--> check si password founie == password_obtenu ?

Moué, cela me semble n'importe quoi. Pourquoi faire simple quand on peut
faire compliqué.

À vrai dire la première methode est parfaites, je ne vois pas où est la
raison de s'inquieter.

--
Guillaume.

Avatar
ftc
Le second système, que j'ai vu dans la plus part des scripts PHP que
j'ai consulté, consiste à aller chercher le mot de passe dans la base
et le comparer directement avec la saisie utilisateur, ça me parait
plus exposé comme solution, mais es-ce le meilleur moyen ?



Pas plus exposé, mais complètement débile dans tous les cas. Les gens
qui écrivent ça n'ont rien compris à l'utilité d'un SGBD. Si on a des
clauses WHERE c'est pas pour faire joli.


Débile ?

Si on utilise un "salt" pour le hashage du mot de passe ( ce qui est
fortement recommandé) et pas seulement les bêtes fonctions md5 ou sha1,
il n'y a pas d'autre solution, il faut commencer par récupérer le mot de
passe crypté pour connaitre le "salt".


Avatar
Patrick Mevzek
<?
$pseudonyme = strip_code($_POST['login']);
// strip_code est une fonction qui retire tout ce qui pourrait etre
dangereux
$password = md5($_POST['password']);


Il faut aussi inclure une donnée secrète (genre une constante de
l'application), et, par exemple, le nom d'utilisateur, dans l'entrée du
MD5, pour ne pas donner que le mot de passe à l'algorithme.
On peut aussi
ajouter une autre variation (comme la date et l'heure de création), mais
alors il faudra évidemment la stocker en clair quelque part.

Pourquoi ? Parce que sinon deux utilisateurs avec le même mot de passe
auront le même mot de passe après passage dans MD5 et stockage dans la
base, ce qui est considéré comme étant mal en général.
Et ceci d'autant plus que MD5 a montré quelques faiblesses.

--
Patrick Mevzek . . . . . . Dot and Co (Paris, France)
<http://www.dotandco.net/> <http://www.dotandco.com/>
Dépêches sur le nommage <news://news.dotandco.net/dotandco.info.news>

Avatar
John GALLET
Débile ?
Oui, débile. Il en traîne partout du code du genre :

...query("SELECT * FROM loosers WHERE id=6";
$row=...fetch
if($row['login']==$_POST['login'] && $row['pass']==$_POST['pass'])...

Enfin, on va dire que ça évite les injections sql dans ces variables
là...

Si on utilise un "salt" pour le hashage du mot de passe ( ce qui est
fortement recommandé) et pas seulement les bêtes fonctions md5 ou sha1,
il n'y a pas d'autre solution, il faut commencer par récupérer le mot de
passe crypté pour connaitre le "salt".


Merci de se rapporter à ce que j'écrivais 10 lignes au dessus :

Est-ce que les données sélectionnées seront utilisées plus loin dans
l'application ?
Si oui, alors c'est la méthode à employer.


Si on gère la crypto dans le langage client, on a besoin des données.

Et dans tous les cas attention, le chiffrage des mots de passe, avec ou
sans salt, ne fait que ralentir l'attaquant qui y a accès, la bonne
méthode, c'est de lui en interdire l'accès.

JG

Avatar
ftc
Débile ?


Oui, débile. Il en traîne partout du code du genre :
...query("SELECT * FROM loosers WHERE id=6";
$row=...fetch
if($row['login']==$_POST['login'] && $row['pass']==$_POST['pass'])...

Enfin, on va dire que ça évite les injections sql dans ces variables
là...


Forcément, avec des exemples de ce genre, oui, c'est complètement débile.

Si on utilise un "salt" pour le hashage du mot de passe ( ce qui est
fortement recommandé) et pas seulement les bêtes fonctions md5 ou sha1,
il n'y a pas d'autre solution, il faut commencer par récupérer le mot de
passe crypté pour connaitre le "salt".



Merci de se rapporter à ce que j'écrivais 10 lignes au dessus :


Est-ce que les données sélectionnées seront utilisées plus loin dans
l'application ?
Si oui, alors c'est la méthode à employer.



Ok, je n'avais pas lu ça dans ce sens. Je pars du principe que ce n'est
pas au SGBD de gérer le chiffrage car ils ont souvent des méthodes de
chiffrage assez basiques et souvent non portables.

Si on gère la crypto dans le langage client, on a besoin des données.

Et dans tous les cas attention, le chiffrage des mots de passe, avec ou
sans salt, ne fait que ralentir l'attaquant qui y a accès, la bonne
méthode, c'est de lui en interdire l'accès.


C'est un prérequis que de sécurisé le reste du code, mais serait bien
présomptueuse la personne qui dirait que jamais on ne pourrait récupérer
les données d'une de ses tables, il fautdonc mettre des batons dans les
roues de l'attaquant à chaque étape.

En matière de sécurité, on dit toujours que le niveau de sécurité global
du système équivaut à celui de élément le plus faible.


Avatar
John GALLET
Re,

Forcément, avec des exemples de ce genre, oui, c'est complètement débile.


bienvenue-dans-la-vie.pasnet. Ce sont justement des "exemples de ce
genre" qu'on voit partout dans la vraie vie.

Est-ce que les données sélectionnées seront utilisées plus loin dans
l'application ?
Si oui, alors c'est la méthode à employer.
Ok, je n'avais pas lu ça dans ce sens. Je pars du principe que ce n'est


pas au SGBD de gérer le chiffrage car ils ont souvent des méthodes de
chiffrage assez basiques et souvent non portables.


C'est en effet une approche tout à fait valable. Mais si tu as deux
applications écrites dans deux langages différents accédants au même
sgbd, perso je laisse faire le SGBD parce que j'ai déjà eut des gags
(dans des cas tordus en particulier sur alpha, ok).

C'est un prérequis que de sécurisé le reste du code, mais serait bien
présomptueuse la personne qui dirait que jamais on ne pourrait récupérer
les données d'une de ses tables, il fautdonc mettre des batons dans les
roues de l'attaquant à chaque étape.


Je n'ai pas dit le contraire, mais le chiffrage des mots de passe
n'apportera clairement pas grand chose, et de toutes façons si on arrive
à extraire cette table là, on arrivera à extraire toutes les données
confidentielles de l'application. Il est trop tard pour faire de la
sécurité quand l'attaquant a récupéré la liste des mots de passe, c'est
foutu. Oui, il faut le faire, mais pour le plaisir de faire chier, je
dirais presque comme "processus de sélection darwinien du
pen-tester/cracker", pas comme mesure réellement efficace.

En matière de sécurité, on dit toujours que le niveau de sécurité global
du système équivaut à celui de élément le plus faible.


Nous sommes tout à fait d'accord, c'est pile poil ce que je dis mot pour
mot dans l'introduction la synthèse du thread du mois de novembre
dernier (à ceci près que pour moi, le maillon le plus faible, c'est
l'humain). Cf.

a++;
JG