Connexion encryptée entre le serveur et le client avec clef privée

Petit nouveau ! | 7 Messages

28 févr. 2009, 02:33

Je propose ici un script de connexion sécurisé pouvant être utilisé pour identifier un utilisateur en passant les informations sur internet cryptées.

J'aimerais vos avis,

Si vous le voulez, vous pouvez utiliser ce script s'il convient à vos besoins.

Si vous voyez une faille quelconque, ou avez des suggestions, n'hésitez pas.

Testé sou IE et Firefox.

Évidement, le Javascript doit être activé.
<?php
// Fichier config.php
// De préférence, ne pas laisser sur le serveur.
// Créé par Stéphane Jacques le 27 février 2009
// Fichier de test d'encryption.
session_start(); // Début de la session

include_once('des.php'); 
/* inclus la librairie DES de Jim Gibbs téléchargeable
 sur http://www.tero.co.uk/des/  */

/*
On définit la clef publique au hasard afin qu'elle ne soit
jamais identique (ou presque).
On attribut aussi un nom et id aléatoires aux champs textes sensibles
afin de brouiller les pistes et ainsi, empêcher la sauvegarde du
mot de passe par le navigateur. Avec un préfixe(ou sufixe), on évite,
le cas échéant, d'avoir 2 valeures identiques pour les noms
et id des champs textes servant au login/mot de passe.
*/
if (!isset($_SESSION['clef_publique']))
{
	$_SESSION['clef_publique'] = rand(rand(), time());
	$_SESSION['input_login']= "a".rand(rand(), time());	
	$_SESSION['input_pass']= "w".rand(rand(), time());	
}

/*
Cette fonction PHP sert à s'assurer que la clef a bien
 24 bits pour le cryptage en DES triple.
*/
function arrange($chaine)
{
	while (strlen($chaine) != 24)
	{
		$a= strlen($chaine) * strlen($chaine);
		if (strlen($chaine) >= 25)
		{
		$chaine = substr($chaine,0,24);
		}
		elseif (strlen($chaine) <= 23)
		{
			$chaine .= $a;
		}
	}
	return $chaine;
}
?>
<!-- Début du code HTML -->
<html>
<head>
<!--
On inclut la bibliothèque DES de Paul Tero disponible
sur son site http://www.tero.co.uk/des/
-->
<script type="text/javascript" src="des.js"></script>
<!--
On inclut la bibliothèque MD5 de Paul Johnston disponible
sur son site http://pajhome.org.uk/crypt/md5
Des réglages peuvent être nécessaires. Référez vous au site
internetde l'auteur.
-->
<script type="text/javascript" src="md5.js"></script>
<script type="text/javascript" language="javascript">
/*
Cette fonction Javascript sert à s'assurer que la clef a bien
 24 bits pour le cryptage en DES triple.
*/
function arrange(chaine)
{
	while (chaine.length != 24)
	{
		var a= chaine.length * chaine.length;
		if (chaine.length >= 25)
		{
			chaine = chaine.substring(0,24);
		}
		else if (chaine.length <= 23)
		{
			chaine += a;
		}
	}
	return chaine;
}
/*
Fonction qui crypte les données du formulaire
 avant de les renvoyer sur internet.
*/
function envoit()
{
	//on désactive le bouton pour éviter plusieurs cliques des fois que ca soit trop long.
	document.getElementById('btn').disabled = 'disabled';

	//entrée des valeures pour les tests.
	document.getElementById('md5client').value = hex_md5(document.getElementById('clef_publique').value);
	document.getElementById('md5clientlabel').innerHTML = hex_md5(document.getElementById('clef_publique').value);
		
	document.getElementById('motdepasse').value = document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value;	
	document.getElementById('motdepasselabel').innerHTML = document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value;	
	
	// On cré la clef de cryptage qui est un hashage de la clef publique ajusté en 24 bits.
	var key = arrange(hex_md5(document.getElementById('clef_publique').value));
	//On assigne le nom d'utilisateur entré par le client comme étant ce qui doit être crypté.
	var message = document.getElementById('<?php echo $_SESSION['input_login']; ?>').value;
	// Encryption des données
	var encrypte = des (key, message, 1,0);
	/*
	On change ce que le client a entré comme nom d'utilisateur pour la valeur
	hexadécimale du cryptage. 
	Il est préférable d'utiliser une valeur hexadécimale pour le transfert sur
	internet, car sinon, il pourrait manquer des bouts du cryptage une fois rendu
	au serveur.
	*/
	document.getElementById('<?php echo $_SESSION['input_login']; ?>').value = stringToHex(encrypte);

	/*
	On cré une clef d'encryption qui est la concaténation
	de la clef privée (c-à-d le mot de passe de l'utilisateur) et la
	clef publique. Toujours ajustée en 24 bits.
	*/
	var keypass = arrange(document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value + document.getElementById('clef_publique').value);
	//On assigne le mot de passe entré par le client comme étant ce qui doit être crypté.	
	var messagepass = document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value;
	//On encrypte les donées.

	var encryptepass = des (keypass, messagepass, 1,0);
	/*
	On change ce que le client a entré comme mot de passe pour la valeur
	hexadécimale du cryptage. 
	Il est préférable d'utiliser une valeur hexadécimale pour le transfert sur
	internet, car sinon, il pourrait manquer des bouts du cryptage une fois rendu
	au serveur.
	*/	
	document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value = stringToHex(encryptepass);

	//On envoit le formulaire au serveur contenant les données cryptées.
	//document.getElementById('formulaire').submit();
}
</script>

</head>
<body>
<?php
if ( isset ($_POST[$_SESSION['input_login']]) || isset ($_POST[$_SESSION['input_pass']]) )
{
	// on vérifie que le formulaire a bien été envoyé.
	if (empty($_POST[$_SESSION['input_login']]) || $_POST[$_SESSION['input_login']] == "0x" )
	{
		$_SESSION = array(); // vide les champs de la session
		session_destroy(); // détruit la session
		echo "ERREUR: Nom d'utilisateur manquant!"; // Un beau message!
		echo "<meta http-equiv=refresh content=\"2; url=config.php\">"; // on redirige vers la page de login
		exit();
	}
	// on vérifie que le formulaire a bien été envoyé.
	if (empty($_POST[$_SESSION['input_pass']]) || $_POST[$_SESSION['input_pass']] == "0x")
	{
		$_SESSION = array(); // vide les champs de la session
		session_destroy(); // détruit la session
		echo "ERREUR: Mot de passe manquant!"; // Un beau message!
		echo "<meta http-equiv=refresh content=\"2; url=config.php\">";  // on redirige vers la page de login
		exit();
	}

	// Début du décryptage des données envoyée par le client.
	// La clef de décryption est un hashage de la clef publique mise en 24 bits.
	$clef = arrange(md5($_SESSION['clef_publique']));
	//On transforme les données encryptées transmises en mode hexadécimal
	//en mode string.
	$donnees_encryptees = hexToString($_POST[$_SESSION['input_login']]);
echo "Nom d'utilisateur re&ccedil;u encrypt&eacute; : ".$_POST[$_SESSION['input_login']]."<br>";
	
	//On décrypte le nom d'utilisateur.
	$utilisateur = trim(des ($clef, $donnees_encryptees, 0, 0, null));
echo "Nom d'utilisateur re&ccedil;u encrypt&eacute; et ensuite d&eacute;crypt&eacute; par le serveur : ".$utilisateur."<br>";
		
	//On définit la clef privée du serveur. Cette clef est très importante!!!
	$clef_privee = trim($_POST['clefprivee']);
	//Avec cette clef, on cré une clef de d'enryptage en 24 bits qui est unique à chaque utilisateur.
	// Je crois que l'on peut dire qu'elle est signée.
	$clef_d_encryption_db = arrange(crypt($clef_privee,$utilisateur));
	//c'est la meme chose
	$clef_de_decryption_db = arrange(crypt($clef_privee,$utilisateur));
	
	$mot_de_passe_recu_en_clair = $_POST['motdepasse'];
echo "Mot de passe re&ccedil;u en clair : ".$mot_de_passe_recu_en_clair."<br>";	

	//on encrypte le mot de passe reçu en clair comme pour le mettre dans la base de données
	$mot_de_passe_db = stringToHex(des ($clef_d_encryption_db, $mot_de_passe_recu_en_clair, 1, 0, null));
echo "<br><b>Mot de passe encod&eacute; pour base de données 
(faire un copier-coller pour le premier utilisateur)</b>
<br> ".$mot_de_passe_db."<br><br>";

	//On décrypte le mot de passe venant de la base de données
	$mot_de_passe_encrypte_db = hexToString($mot_de_passe_db);
	$mot_de_passe = des ($clef_de_decryption_db, $mot_de_passe_encrypte_db, 0, 0, null);
echo "Mot de passe de la base de donn&eacute; d&eacute;crypt&eacute; : ".$mot_de_passe."<br>";
	
	//On remet le mot de passe recu du client en string.
	$mot_de_passe_recu_encrypte = hexToString($_POST[$_SESSION['input_pass']]);
echo "Mot de passe re&ccedil;u encrypt&eacute; : ".$_POST[$_SESSION['input_pass']]."<br>";

//On cré la clef de décryptage à l'aide du mot de passe décryptée de la base de données.
	$clef_de_decryption = arrange(trim($mot_de_passe).$_SESSION['clef_publique']);

	//On décrypte le mot de passe envoyé par le client.
	$mot_de_passe_recu_decrypte = des ($clef_de_decryption, $mot_de_passe_recu_encrypte, 0, 0, null);
echo "Mot de passe re&ccedil;u encrypt&eacute;, d&eacute;crypt&eacute; par le serveur : ".$mot_de_passe_recu_decrypte."<br>";	

	//On comparre les mots de passes
	if (trim($mot_de_passe) == trim($mot_de_passe_recu_decrypte))
	{
		echo "<br><h2>R&eacute;sultat : OK!</h2><br>";
	}
	else
	{
		echo "<br><h2>R&eacute;sultat : Il y a eu une erreur!</h2><br>";
	}
	
	echo "<br>
	Hashage MD5 de la clef publique re&ccedil;u : ".$_POST['md5client']."<br>
	<br>
	Hashage MD5 de la clef publique par le serveur : ".md5($_SESSION['clef_publique'])."<br>
	<br>
	<br>	
	<a href=\"config.php\"> R&eacute;essayer</a>
	";
	$_SESSION = array(); // vide les champs de la session
	session_destroy(); // détruit la session
}
else
{
?>
<form id="formulaire" name="formulaire" action="config.php" method="POST">
<p>
Page de test et d'encryption pour le mot de passe de l'administrateur du site.<br>
Permet aussi de comparer les résultats du hashage MD5 entre le client et le serveur.
</p>

Clef publique cr&eacute;&eacute;e par le serveur : <input type="text" name="clef_publique" id="clef_publique" size="20" disabled value="<?php echo $_SESSION['clef_publique']; ?>"><br>
<br>
Nom d'utilisateur : <input type="text" size="20" id="<?php echo $_SESSION['input_login']; ?>" name="<?php echo $_SESSION['input_login']; ?>"><br>
Mot de passe : <input type="text" size="20" id="<?php echo $_SESSION['input_pass']; ?>" name="<?php echo $_SESSION['input_pass']; ?>"><br>
Clef privee que vous désirez utiliser : <input type="text" name="clefprivee" id="clefprivee" size="100"><br>
<br>
Mot de passe en clair (pour le décryptage. Ce champ est normalement inexistant.) : <label id="motdepasselabel"></label>
<input type="hidden" id="motdepasse" name="motdepasse">
<br>
Résultat du hashage MD5 de la clef publique : <label id="md5clientlabel"></label>
<input type="hidden" name="md5client" id="md5client"><br>
<br>
<br>
<input type="button" name="btn" id="btn" value="&Eacute;tape 1 - Crypter les donn&eacute;es" onclick="envoit();"><br>
<br>
<input type="submit" name="envoyer" id="envoyer" value="&Eacute;tape 2 - Envoyer le formulaire">
<?php
}
?>

</body>
</html>
Voici un exemple de connexion utilisant cet encryption:
<!--
   Fichier login.php
   Créé par Stéphane Jacques le 26 février 2009
   Fichier de connexion type.
-->

<!-- Début des scripts servants a l'encryption -->

<?php
session_start(); // Début de la session

include_once('des.php'); 

if (!isset($_SESSION['clef_publique']))
{
	$_SESSION['clef_publique'] = rand(rand(), time());
	$_SESSION['input_login']= "a".rand(rand(), time());	
	$_SESSION['input_pass']= "w".rand(rand(), time());	
}

function arrange($chaine)
{
	while (strlen($chaine) != 24)
	{
		$a= strlen($chaine) * strlen($chaine);
		if (strlen($chaine) >= 25)
		{
		$chaine = substr($chaine,0,24);
		}
		elseif (strlen($chaine) <= 23)
		{
			$chaine .= $a;
		}
	}
	return $chaine;
}
?>
<!-- Début du code HTML -->
<html>
<head>
<script type="text/javascript" src="des.js"></script>
<script type="text/javascript" src="md5.js"></script>

<script type="text/javascript" language="javascript">
function arrange(chaine)
{
	while (chaine.length != 24)
	{
		var a= chaine.length * chaine.length;
		if (chaine.length >= 25)
		{
			chaine = chaine.substring(0,24);
		}
		else if (chaine.length <= 23)
		{
			chaine += a;
		}
	}
	return chaine;
}

function envoit()
{
	document.getElementById('btn').disabled = 'disabled';

	var key = arrange(hex_md5(document.getElementById('clef_publique').value));
	var message = document.getElementById('<?php echo $_SESSION['input_login']; ?>').value;
	var encrypte = des (key, message, 1,0);

	document.getElementById('<?php echo $_SESSION['input_login']; ?>').value = stringToHex(encrypte);

	var keypass = arrange(document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value + document.getElementById('clef_publique').value);
	var messagepass = document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value;
	var encryptepass = des (keypass, messagepass, 1,0);

	document.getElementById('<?php echo $_SESSION['input_pass']; ?>').value = stringToHex(encryptepass);

	document.getElementById('formulaire').submit();
}
</script>

</head>

<body>
<?php

// on vérifie que le formulaire a bien été envoyé.
if ( isset ($_POST[$_SESSION['input_login']]) || isset ($_POST[$_SESSION['input_pass']]) )
{
	// on vérifie que le formulaire a bien été envoyé.
	if (empty($_POST[$_SESSION['input_login']]))
	{
		$_SESSION = array(); // vide les champs de la session
		session_destroy(); // détruit la session
		echo "ERREUR: Nom d'utilisateur manquant!"; // Un beau message!
		echo "<meta http-equiv=refresh content=\"10; url=login.php\">"; // on redirige vers la page de login
		exit();
	}
	// on vérifie que le formulaire a bien été envoyé.
	if (empty($_POST[$_SESSION['input_pass']]))
	{
		$_SESSION = array(); // vide les champs de la session
		session_destroy(); // détruit la session
		echo "ERREUR: Mot de passe manquant!"; // Un beau message!
		echo "<meta http-equiv=refresh content=\"10; url=login.php\">";  // on redirige vers la page de login
		exit();
	}


	$clef = arrange(md5($_SESSION['clef_publique']));
	$donnees_encryptees = hexToString($_POST[$_SESSION['input_login']]);
	$utilisateur = trim(des ($clef, $donnees_encryptees, 0, 0, null));

	$clef_privee = "la clef privée du serveur";
	$clef_de_decryption = arrange(crypt($clef_privee,$utilisateur));

	// Les parametres de connexion à MySQL.
	$mysql_serveur = "Votre serveur MySQL"; //Normalement, "localhost"
	$mysql_user = "Nom d'utilisateur MySQL";
	$mysql_mot_de_passe = "Mot de passe MySQL";
	$mysql_base_de_donnees = "Nom de la base de donnée";

	//On ouvre la conexion au serveur MySQL de façon sécurisée afin de se protéger
	// des injections SQL.
	$connexion = mysql_connect ($mysql_serveur,$mysql_user,$mysql_mot_de_passe) or die ('Erreur connexion: '.mysql_error() ); 
	if(!is_resource($connexion))
	{
		$_SESSION = array(); // vide les champs de la session
		session_destroy(); // détruit la session
        echo "&Eacute;chec de la connexion au serveur\n";
		//on retourne sur la page de login
        echo "<meta http-equiv=refresh content=\"2; url=login.php\">";
        exit();
    }
    else
    {
		if(get_magic_quotes_gpc())
		{
			$objet_de_recherche = stripslashes($utilisateur);
		}
		else
		{
			$objet_de_recherche = $username;
		}

		//On va chercher les informations nécessaires dans la table des utilisateurs
		$requete = sprintf("SELECT * FROM `%s`.`nom_de_la_table`
						WHERE `champ_login` = '%s' LIMIT 1;",
                    $mysql_base_de_donnees,
                    mysql_real_escape_string($objet_de_recherche, $connexion));                
        $resultats = mysql_query($requete, $connexion);
		// Si la requête retourne quelque chose, c'est que l'utilisateur existe.
		//Évidemment, lors de la création des utilisateurs, il faut s'assurer qu'ils sont uniques.
		if (mysql_num_rows($resultats) == 1)
		{
			while ($data=mysql_fetch_array($resultats))
			{
				//On décrypte le mot de passe venant de la base de données

				$mot_de_passe_encrypte_db = hexToString($data['champ_de_mot_de_passe']);
				$mot_de_passe = des ($clef_de_decryption, $mot_de_passe_encrypte_db, 0, 0, null);
				// On récupère en même temps les données nécessaires pour la session si le cas est.
				$id = $data['champ_voulu'];

			}
		}
		else 
		{
			//la requête n'a pas trouvé l'utilisateur, donc il n'existe pas.
			$_SESSION = array(); // vide les champs de la session
			session_destroy(); // détruit la session
			echo "ERREUR :<br> Nom d'utilisateur ou mot de passe incorrecte!"; //encore un beau message!
			//on sur la page de login
			echo "<meta http-equiv=refresh content=\"2; url=login.php\">";
			exit();
    	}
	}


	$mot_de_passe_recu_encrypte = hexToString($_POST[$_SESSION['input_pass']]);
	$clef_de_decryption = arrange(trim($mot_de_passe).$_SESSION['clef_publique']);
	$mot_de_passe_recu_decrypte = des ($clef_de_decryption, $mot_de_passe_recu_encrypte, 0, 0, null);

	if (trim($mot_de_passe) == trim($mot_de_passe_recu_decrypte))
	{
		//Les mots de passes sont pareils!
		$_SESSION = array(); //On vide les informations actuelles de la session.
		//on assigne aux variables sessions les informations nécessaires au fonctionnement du site.
		$_SESSION['id_user'] = $id;
		$_SESSION['login'] = $nom_utils;
		echo "<h2>Acc&egrave;s autoris&eacute;!</h2><br />\n"; // ce que tout le monde veux voir comme message
		echo "<h4>Vous allez être redirigé dans 2 secondes.</h4>\n"; // un dernier p'tit message avant les portes du paradis!
		//on redirige l'utilisateur vers la page désirée.
		echo "<meta http-equiv=refresh content=\"2; url=pagedesiree.php?PHPSESSID=".session_id()."\">";
	}
	else 
	{
		// Les mots de passes ne sont pas identiques!
		$_SESSION = array(); // vide les champs de la session
		session_destroy(); // détruit la session
		echo "ERREUR 2: Nom d'utilisateur ou mot de passe incorrecte!"; // un petit message redondant!
		echo "<meta http-equiv=refresh content=\"10; url=login.php\">";
		exit();
	}
}
else
{
	//Le formulaire n'a pas encore été envoyé.
?>
<form action="login.php" method="post" name="formulaire" target="_top" id="formulaire">
  <table width="300" border="1" align="center" cellpadding="5" cellspacing="0">
    <tr> 
      <td width="98">Utilisateur :</td>
      <td width="176" align="center" valign="top">
      	<input type="text" size="20" id="<?php echo $_SESSION['input_login']; ?>" name="<?php echo $_SESSION['input_login']; ?>">
      </td>
    </tr>
    <tr> 
      <td>Mot de passe :</td>
      <td align="center" valign="top">
      	<input type="text" size="20" id="<?php echo $_SESSION['input_pass']; ?>" name="<?php echo $_SESSION['input_pass']; ?>">
      </td>
    </tr>
    <tr align="center"> 
      <td colspan="2"> 
        <input type="hidden" name="clef_publique" id="clef_publique" value="<?php echo $_SESSION['clef_publique']; ?>">
        <input type="button" name="btn" id="btn" value="Connexion" onclick="envoit();"/></td>
      </td>
    </tr>
  </table>			  
</form>
<?php
}
?>
</body>
</html>
Voila!
Si vous voyez une faille, n'hésitez pas a me le dire!

Merci!

Petit nouveau ! | 7 Messages

03 mars 2009, 04:29

Aucun commentaire?

ViPHP
ViPHP | 4674 Messages

03 mars 2009, 04:35

Hey :-),

Bah je me demande à quoi ça peut servir en fait :-k
Normalement, toutes les connexions se font sur le serveur (connexions au serveur de bases de données par exemple). La seule connexion entre le client et le serveur, c'est pour les sessions. On sait sécuriser ça d'une meilleure façon qu'avec du Javascript … Et au « pire », on a des protocoles comme HTTPS. Donc oui, je me pose des questions sur l'intérêt de la chose (que tu n'as pas expliqué) :-).
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 5924 Messages

03 mars 2009, 09:42

Quelques remarques tout de même :
- Si si, entre le client et le serveur, il y a des mots de passe et autres données confidentielles qui peuvent circuler.
- Et le protocole https pose quelquefois certains problèmes (le certificat qui est payant, nécessite la main sur le serveur, il ne peut pas y avoir plusieurs sites https sur un même serveur (attention, je simplifie un peu tout de même)).
- Mais, les fautes d'orthographe grossières dans les noms de fonctions, c'est juste insupportable, et rien que pour cela, je mettrais ta librairie à la poubelle…

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

03 mars 2009, 10:10

En ce qui me concerne, c'est le fait que tout soit mélanger entre PHP et HTML qui m'a découragé dès le départ.

C'est domage, car du coup, j'ai même pas idée de la qualité de ton code.
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer

Petit nouveau ! | 7 Messages

03 mars 2009, 14:13

Merci pour ces commentaires constructifs.

Je vous l'accorde, je n'ai pas spécifié l'utilité.

Comme Sékiltoyai l'a si bien mentionné, il y a des mots de passes et autres informations personelles qui circulent sur l'internet. Donc, si l'on peut cacher ces informations, cela évite qu'une personne mal intentionnée récupère ces informations. Oui, il y a les connexions https, mais elles peuvent s'avérer dispendieuses si l'on veut avoir un certificat.

Ensuite, c'est une encryption qui n'utilise pas MCRYPT (étant un module complémentaire qui n'est pas installé par défaut). Donc, c'est une solution qui peut être aussi utilisé en local sur le serveur.

Pour les fautes d'orthographes, j'en suis désolé. J'avoue que je n'ai pas pris le temps de me corriger.

Pour le html mélangé avec le php, pourriez-vous me mettre sur la piste de comment je devrais faire afin d'intégrer le php, javascript et html de façon fluide afin d'en facilité la lecture? Je ne demande pas de reprendre mon code, mais juste me donner un exemple. En utilisant un "echo", je perds la fonction de correction de code et de l'affichage en couleur des balises des logiciels comme eclipse ou dreamweaver. Si vous avez une meilleure idée, laissez le moi savoir. J'ai soif d'apprendre.