Chiffrement et hash en PHP contre l'attaque Man in the middle


précédentsommairesuivant

V. Chiffrement des données

Nous avons vu comment protéger le mot de passe lors de la connexion. Cependant, on peut vouloir protéger d'autres données que l'on souhaite garder en clair, comme l'email ou certaines informations personnelles. Pour cela, nous pouvons utiliser des algorithmes de chiffrement largement éprouvés.

V-A. Algorithmes existants

V-A-1. Chiffrement symétrique

Aussi appelé "à clé privée" car il est fondé sur le principe simple qu'un code est indéchiffrable tant qu'on ne connaît pas la clé (normal ;-).
C'est le système de chiffrement le plus connu, il est vieux comme le monde et a servi à toutes les époques, pour tous les contextes : personnel, industriel, militaire (qui ne connait pas enigma ?), services secrets...
C'est aussi le plus simple à appréhender car fondamentalement, tout ce qu'on ne comprend pas est plus ou moins déchiffrable par un code, le meilleur exemple qui me passe par la tête étant l'ADN. Nous avons appris à lire pour en tirer un "code génétique", maintenant, nous écrivons grâce à ce code. L'inconvénient du chiffrement symétrique, c'est qu'il faut que le destinataire ET l'expéditeur connaissent le même code, sinon, comme pour l'ADN, il faudra chercher. Ils devront donc à un moment ou un autre communiquer ensemble de manière non chiffrée, au moins pour s'échanger le code de chiffrement.
Pour donner une image, ça correspondrait à envoyer par la poste la clé d'un coffre fort de banque.

Pour permettre l'échange de cette clé, on utilise le chiffrement asymétrique que nous allons voir maintenant.

Parmi les algorithmes de chiffrement symétrique, on trouve le DES, le tripleDES, le Blowfish, le RC2 à RC6, le IDEA...

V-A-2. Chiffrement asymétrique

Aussi appelé "à clé publique", même si en fait, on manipule une paire de clés : une publique et une privée.
Il est asymétrique car ne fonctionne que dans un seul sens. Par exemple :

  • A veut recevoir une information provenant de B.
  • A génère une clé publique et une clé privée.
  • A envoie la clé publique à B.
  • B chiffre son information avec la clé publique.
  • B envoie le message chiffré à A.
  • A déchiffre en utilisant la clé privée.

La génération de ces clés est fondée sur la difficulté de factorisation des grands nombres.
En gros, ça veut dire qu'on peut aisément multiplier 2 énormes nombres entre eux, mais qu'on ne peut pas retrouver si facilement ces 2 nombres en ayant que le résultat de cette multiplication.
En pratique, on choisi 2 grands nombres premiers. Il faut qu'ils soient premiers pour que leur multiplication ne donne qu'une seule factorisation possible.
Par exemple 9 et 6 sont très mauvais, car 9x6=54, or 54 est aussi égal à 27x2, 9x6, 3x18.

Les algorithmes de chiffrement asymétrique sont beaucoup plus gourmands que ceux symétriques, on les utilise donc pour chiffrer les informations essentielles, comme par exemple, la clé de chiffrement privée d'un chiffrement symétrique.
Les deux types d'algorithmes sont donc totalement complémentaires.

Parmi les algorithmes de chiffrement asymétrique, on trouve le RSA (le plus connu), le PGP, le DSA...

V-B. Cas pratique avec le RSA

Nous allons ici voir comment chiffrer une donnée provenant d'un formulaire utilisateur.
Tout d'abord, il convient de bien situer le but.
Je rappelle que cette partie concerne les données que l'on souhaite chiffrer coté client afin de ne pas les faire circuler sur le réseau en clair, mais qu'un hash n'est pas possible car nous avons besoin de cette donnée en clair coté serveur.
Chronologiquement, ça va se dérouler de la sorte.

Déroulement du chiffrement asymétrique d'une donnée distante
  • Le serveur génère une paire de clés publique/privée.
  • Il garde la clé privée (en session ou en base)
  • Il envoie la clé publique avec le formulaire demandant la donnée à chiffrer.
  • On chiffre côté client en utilisant la clé publique.
  • On envoie le formulaire contenant uniquement la donnée chiffrée (on retire évidemment celle saisie par l'utilisateur en clair).
  • On déchiffre côté serveur avec la clé privée pour récupérer notre donnée.

Nous avons donc besoin de générer la paire de clés en PHP, de chiffrer en Javascript et de déchiffrer en PHP.

Une clé est en fait constituée de 2 valeurs, un module et un exposant.

Coté PHP

Pour la partie PHP, nous utilisons le package Crypt_RSA (version 1.2.0b) de PEAR dont le lien est disponible en fin d'article.
Il utilise les extensions PHP de gestion de grands nombres. BCMath par défaut (la plus lente, mais la plus courante), BigInt ou GMP (classées ici de la plus lente à la plus rapide).
Elle est compatible PHP4 et PHP5.

J'y ai apporté quelques modifications. Ces modifications sont visibles dans les annexes.
Le but principal de ces modifications est de permettre la transmission de PHP vers JS et inversement en utilisant des nombres et non des hiéroglyphes.

Coté Javascript

À l'image du package Crypt_RSA de PEAR, j'ai utilisé une librairie de gestion de grands nombres auquel j'ai ajouter quelques fonctions nécessaires pour coller avec notre modèle en PHP.
Au final, on a 2 fonctions principales. Une pour charger notre clé publique créée par PHP, l'autre pour encrypter.

 
Sélectionnez

function RSAPublicKey(encryptionExponent, modulus, nbBits)
function encryptedString(key, s)

Utilisation

Procédons par ordre chronologique. Tout d'abord, il nous faut générer les clés. Si vous vous souvenez bien, ceci est la charge du serveur, donc de Crypt_RSA.

Code de génération des clés
Sélectionnez

set_include_path('./PEAR/');

session_start();

// Le package Crypt_RSA de PEAR
require_once 'Crypt/RSA.php';

// Les modifs apportées sur ce package
require_once './class/myKey.php';
require_once './class/myKeyPair.php';
require_once './class/myRSA.php';

$nbBits = 128; 
$key_pair = new Crypt_RSA_KeyPair($nbBits, 'BCMath');
$public_key = $key_pair->getPublicKey();
$private_key = $key_pair->getPrivateKey();
    
// on met en session la clé privée
$_SESSION['private_module'] = $private_key->getModulus();
$_SESSION['private_exp'] = $private_key->getExponent();

Remarquez que nous mettons la clé privée en session.

Il nous faut maintenant passer la clé publique au Javascript.

Passage de la clé publique au js
Sélectionnez

<script language="JavaScript" src="./js/BigInt.js"></script>
<script language="JavaScript" src="./js/BigIntMath.js"></script>
<script language="JavaScript" src="./js/RSA.js"></script>
<script language="JavaScript">
var key;
var EncryptedText;

// La clé publique passées aux js
var public_exponent = "<?php echo $public_key->getIntExponent(); ?>";
var public_modulus =  "<?php echo $public_key->getIntModulus(); ?>";

var nb_bits = <?php echo $nbBits; ?>;

// Initialisation de BigInt
setMaxDigits(154);

// Création de la clé publique
public_key = new RSAPublicKey(public_exponent, public_modulus, nb_bits);

Nous définissons maintenant 2 fonctions, l'une pour récupérer la valeur saisie par l'utilisateur, la chiffrer et la replacer dans le champ (en modifiant légèrement les propriétés du champ au passage), l'autre pour annuler le chiffrement.

fonction de contrôle du formulaire
Sélectionnez

function encrypt()
{
    var code_str = document.register.codesecret.value;
    EncryptedText = encryptedString(public_key, code_str);
    document.register.codesecret.type='text';
    document.register.codesecret.size=EncryptedText.length; 
    document.register.codesecret.value=EncryptedText;
    document.register.isCrypted.value=1;
}

function annule()
{
    document.register.codesecret.type='password';
    document.register.codesecret.size=15; 
    document.register.codesecret.value='';
    document.register.isCrypted.value=0;
} 

Enfin, il nous faut un formulaire utilisateur classique.

Formulaire utilisateur
Sélectionnez

<form id="register" method="post" action="" name="register">
    <fieldset>
	    <legend>Saisissez votre code secret</legend>
		<div>
			<label>
				<strong>Code secret : </strong><br />
				<input type="password" name="codesecret" size="16" autocomplete="off"/><br />
			</label>
		</div>
		<br />
        <input type="button" value="Chiffrer votre code secret" onclick="encrypt()" class="bouton">
        <input type="button" value="Annuler le chiffrement" onclick="annule()" class="bouton">
        <input type="hidden" name="isCrypted" value="0">
    </fieldset>
    <p align="center"><input type="submit" name="register" value="Valider" class="bouton" /></p>
</form>

Rien d'exceptionnel, juste à noter un champ caché qui permettra au PHP de savoir si le champ est chiffré ou non.

Et pour finir, la récupération du formulaire, avec le déchiffrement du champ si besoin.

traitement du formulaire
Sélectionnez

if ( ! empty($_POST['codesecret']))
{
    $codesecret = $_POST['codesecret'];
    if ( (isset($_POST['isCrypted'])) && ($_POST['isCrypted'] == 1) )
    {
        $rsa_obj = new Crypt_RSA(null, 'BCMath');
        $private_key = new Crypt_RSA_Key($_SESSION['private_module'], $_SESSION['private_exp'], 'private', 'BCMath');
        $codesecret = $rsa_obj->decrypt($codesecret, $private_key);
    }
    
    echo 'Votre code secret : '.$codesecret;
}

Testez ce script vous même.

Pour reconstituer cet exemple, il vous faudra :
- [ Les sources de l'exemple ].
- [ Le package PEAR ]
- [ Le package Crypt_RSA ]

Renommez les répertoires de PEAR et Crypt_RSA de façon à obtenir cette arborescence.

Arborescence du dossier du projet

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 25/01/2007 Guillaume Affringue. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.