Can I Trust This Form ?

Petite question toute simple mais qui finalement est sujette à beaucoup d’interrogations !
Comment peut on être sur que l’administrateur du site ne conserve pas nos mots de passe en clair pour pouvoir s’en resservir ailleurs ? Nous sommes en 2014 il est temps de rendre à l’utilisateur le pouvoir de sécuriser ses donnés !

Bad Guy Corner

Prenons un rapide exemple, un formulaire d’inscription basique :

<form action="submit_login.php" method="post">
<input name="login" type="text">
<input name="password" type="password">
<input type="submit" value="S'enregistrer">
</form>

Et le formulaire PHP qui lui est lié :

$dns = 'mysql:host=localhost;dbname=evil_website';
$utilisateur = 'root';
$motDePasse = '';
$bdd = new PDO( $dns, $utilisateur, $motDePasse );

if(isset($_POST['login']) &amp;&amp; $_POST['login']!='' &amp;&amp; isset($_POST['password']) &amp;&amp; $_POST['login']!='')
{
 $insert=$bdd-&gt;prepare("insert into users(login,password) values (:login_user, :password_user)");
 $insert-&gt;execute(array(
      'login_user'=&gt;$_POST['login'],
      'password_user'=&gt;$_POST['password']
  ));

}

Plusieurs remarques :

  • Je devrais mettre un mot de passe sur ma bdd.
  • Je devrais utiliser un compte avec moins de privilèges.
Voyons un peu ce que cela donne dans la base de donnés :
Comme vous pouvez le voir j’ai donc accès aux mots de passe « en clair ».

Alors me direz vous la bonne pratique est de hasher ces mots de passe en md5 par exemple.
Car comme on l’entend « si jamais j’ai une SQLi le méchant il aura pas les mots de passe ».
Cette pratique étant répandu l’utilisateur se dit alors que si un pirate ne peut pas accéder aux véritables mots de passe personne ne le peux !

Ce qui est totalement faux :
if(isset($_POST['login']) &amp;&amp; $_POST['login']!='' &amp;&amp; isset($_POST['password']) &amp;&amp; !empty($_POST['login']))
{
 $insert=$bdd-&gt;prepare("insert into users(login,password) values (:login_user, :password_user)");
 $insert-&gt;execute(array(
      'login_user'=&gt;$_POST['login'],
      'password_user'=&gt;md5($_POST['password'])
  ));

 $monfichier = fopen('pwn_you.txt', 'r+');
 fputs($monfichier, 'Login :'.$_POST['login'].' Password :'.$_POST['password']."n");

}

Comme vous le voyez rien ne m’empêche de stocker ces informations dans un fichier texte tout en ayant leurs versions hashés dans la base de donnés !

Ok type your password i’m not watching !

Alors que faire ? Pourquoi ne pas tout simplement empêcher l’administrateur du site d’avoir lui même la vrai version du mot de passe en hashant ce dernier coté client ?
Avec du jquery/ajax par exemple :

<title>My Insecure form gets PWNED !</title>  
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js" type="text/javascript"></script>  
 <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js"></script>  
  
  
<form action="submit_login.php" id="formulaire" method="post">  
 <input id="login" name="login" type="text">  
 <input id="password" name="password" type="password">  
 <input id="envoyer" type="submit" value="S'enregistrer">  
</form>  
<div id="resultat">  
</div>  
<script>  
 $(document).ready(function() {  
    // lorsque je soumets le formulaire  
    $('#formulaire').on('submit', function() {  
        var $this = $(this);  
   
        // je récupère les valeurs  
        var login = $('#login').val();  
        var password = $('#password').val();  
        $('#password').val(CryptoJS.MD5(password));  
  
        if(login === '' || password === '') {  
            alert('Les champs doivent êtres remplis');  
        } else {  
            // appel Ajax  
            $.ajax({  
                url: $this.attr('action'), // le nom du fichier indiqué dans le formulaire  
                type: $this.attr('method'), // la méthode indiquée dans le formulaire (get ou post)  
                data: $this.serialize(), // je sérialise les données   
                success: function(html) { // je récupère la réponse du fichier PHP  
                    if(html=="ok")  
                    {  
                     $("#resultat").html("Succes and secure !");  
                    }  
                    else  
                    {  
                     $("#resultat").html("Error");  
                    }  
                }  
            });  
        }  
        return false; // j'empêche le navigateur de soumettre lui-même le formulaire  
    });  
});  
</script>  

Voici la requête qui est alors envoyé par le navigateur :

Il deviens donc impossible pour le serveur d’obtenir le mot de passe en clair !
L’administrateur peut encore salter ce hash avant de le réhasher, il devra juste se souvenir que pour verifer le mot de passe utilisateur il faut hasher deux fois, sauf si on applique ce même procédé sur les formulaires de login ( et globalement tout ceux demandant un mot de passe )

Et la sécurité coté serveur alors ? Ne l’oublions surtout pas, il suffit de créer une fonction de vérification de md5 afin de s’assurer que nous avons bien les données dans un bon format :

function isValidMd5($md5)
{
    return preg_match('/^[a-f0-9]{32}$/', $md5);
}

En voyant un peu plus loin cela limite aussi le risque engendré par du Man in the Middle.
Evidemment la mise en place de SSL est encore meilleure pour cela mais si je vous dit Heartbleed vous comprendrez pourquoi ce n’est pas une réponse à tout.

Si ce genre de procédé devenait « standard » cela augmenterait le niveau de confiance sur le net.
Ce genre de code est surement améliorable, n’étant pas un expert dev-web..

Enfin j’espère que cet article vous aura plu ! N’hésitez pas à me faire des remarques dans la partie commentaires !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *