Lorsque vient le temps de protéger nos données plusieurs options s’offrent à nous. En fait, plus précisément deux options. Il y a d’un côté le hachage et de l’autre l’encryption. Il existe quelques subtilité entre les deux méthodes qui font en sorte que le choix varie selon la situation.

Hachage

Le hachage est le résultat d’un algorithme qui analyse une chaine de caractère pour la rendre incompréhensible à la lecture. Un algorithme de hachage génère une valeur unique à partir de l’entrée envoyé. L’avantage (ou le désavantage selon certain) du hachage c’est qu’une fois encodé, il est pratiquement impossible de retourner en arrière pour connaître la valeur de départ. Autrement dit, c’est un encryptage unidirectionnel.

Le hachage est souvent utilisé pour l’encryptage de mot de passe dans les base de données. En utilisant ce procédé, il est impossible pour un visiteur ou un administrateur de la BD de connaître les mots de passes des utilisateurs.

Il existe présentement deux fonctions de hachage disponible avec PHP soit md5 et sha1. Il est conseillé d’ajouter une chaîne unique à chaque encryptage communément appelé salt. Cette chaîne permettra de ne pas avoir deux fois le même mot de passe dans la bd. Cela augmentera donc notre protection de nos utilisateurs. Cette chaîne doit être un élément propre à chaque utilisateur. Je propose dans le code suivant d’utiliser la fonction uniqid(). On doit par contre enregistrer le salt avec notre nouvel utilisateur dans la db. Comme je le disais plus haut, cet astuce est uniquement pour éviter une duplication des mots de passes.


//un mot de passe
$mdp = '7chd90sh';
echo md5($mdp); //ab96b777b2f826b110fedffaa9d400ed

//avec un salt
$salt = uniqid();
echo md5($salt.$mdp); //94dd45698e51cfe3995eb3a26de09af3

Encryptage

L’encryptage contrairement au hachage permet d’encoder des données et de les décoder. Cela nous permet d’emmagasiner des données tout en sachant que nous allons pouvoir les récupérer au moment opportun sans problème.

Comment encrypter nos données en PHP?

Il existe une librairie dénommée Mcrypt qui permet d’encrypter nos données. Elle utilise les différents algorithmes d’encryptage disponible. Par contre, elle n’est pas disponible sur tous les hébergeurs de site web, surtout les ceux gratuit. Je vous conseils donc de vérifier avec un phpinfo() si vous hébergeurs vous permet d’utiliser la librairie MCrypt. Pour ceux qui ont accès au php.ini de leur serveur, vous n’avez qu’à l’activer. Pour utiliser la librairie MCrypt, nous avons besoin de trois choses essentiel avant même de commencer à coder. Tout d’abord, nous avons besoin d’un algorithme d’encryptage. Ensuite nous avons besoin d’un mode d’encryptions et pour finir il nous faut une clé secrète. Cette clé sera l’élément essentiel qui vous permettra de récupérer vos données encryptés. Si vous perdez cette clé, vous données seront encrypté à jamais. Si vous vous faites voler votre clé, la barrière de sécurité est sérieusement compromise. Je vais vous présenter la classe PHP que j’utilise pour encrypter mes informations privées. Cette classe a été créée pour le livre Pro PHP Security de la maison d’édition Apress. Un livre que je conseil fortement au développeur qui souhaite en apprendre plus sur la sécurité sur le web. Pour commencer voici les déclarations de variables de la classe ainsi que le constructeur de la classe.


public function __construct( $algo='aes' ) {
      switch ( $algo ) {
      case 'aes':
        $algorithme = MCRYPT_RIJNDAEL_256;
        break;
      case 'tripledes':
        $algorithme = MCRYPT_TRIPLEDES;
        break;
      case 'blowfish':
        $algorithme = MCRYPT_BLOWFISH;
        break;
      default:
        // disallow any other algorithm
        exit( "Erreur fatale. Cette classe n'accepte pas l'algorithme : $algo.
               Vous devez utiliser une de ces algo : 'aes','tripledes', ou 'blowfish'." );
      }
      $this->algo = $algorithme;

      // création d'une ressource mcrypt
      $this->mcrypt = mcrypt_module_open( $this->algo, '',  MCRYPT_MODE_CBC, '' );

      // détermine la taile du vecteur d'initialisation selon le couple algo/mode
      $this->ivsize = mcrypt_enc_get_iv_size( $this->mcrypt );

      // détermine la taille maximal de la clé
      $this->maxKeysize = mcrypt_enc_get_key_size( $this->mcrypt );
    }

Ensuite nous devons assigner une clé à notre classe. Cette clé subira plusieurs manipulations qui feront en sorte qu’elle sera encodée dans la mémoire. Elle sera tout d’abord haché pour être ensuite transformé en binaire.


public function setKey( $secret ) {
	  // initialize key
      $key = NULL;

      // Détermine le nombre de block de 32 caractère que nous aurons besoin
      $keyblocks = ceil( ( $this->maxKeysize * 2 ) / 32 );

      // pour chaque block, on doit généré une chaîne passé au md5 qui encodera la clé
      for ( $ix = 0; $ix < $keyblocks; $ix++ ) {
        $key .= md5( $ix . $secret );
      }

      // on "pack" ensuite la clé en mode binaire (2:1 ratio)
      $key = pack( 'H*', $key );

      // on coupe la clé selon la taille maximal de notre algorithme
      $this->key = substr( $key, 0, $this->maxKeysize );
	  $this->keysize = strlen( $this->key );
    }

Après il nous faut une fonction d’encodage et une autre de décodage. Lorsque la donnée sera encodée, on y glisse le vecteur d’initialisation pour permettre d’avoir exactement le même lors du décodage.


public function encrypt( $data ) {
      // génère un vecteur d'initialisation
      $this->iv = mcrypt_create_iv( $this->ivsize, MCRYPT_RAND );

      // inititialisation
      if (mcrypt_generic_init( $this->mcrypt, $this->key, $this->iv ) === -1) {
        $this->__destruct();
        exit( 'Erreur fatale, incapable de créer d\'initialiser l\'encryptage.' );
      }

      // on encrypte
      $ciphertext = mcrypt_generic( $this->mcrypt, $data );

      // on détruit la ressource mcrypt
      mcrypt_generic_deinit( $this->mcrypt );

      // prepend le vecteur d'initialisation
      $out = $this->iv.$ciphertext;

      // encode le text encrypté avec un base64
      // on coupe le texte en block de 64 caractère pour la transmission
      $out = chunk_split( base64_encode( $out ), 64 );

      return $out;
    }

    public function decrypt( $data ) {
      // la ligne doit être en base64
      $input = base64_decode( $data );

      // lon récupère la taille du vecteur d'initialisation contenu dans notre texte encodé
      $this->iv = substr( $input, 0, $this->ivsize );
      $ciphertext = substr( $input, $this->ivsize );

      // initialisation
      if ( mcrypt_generic_init( $this->mcrypt, $this->key, $this->iv ) === -1) {
        $this->__destruct();
        exit( 'Erreur fatale, incapable d\'initialiser la routine d\'encryption.' );
      }

      $out = mdecrypt_generic( $this->mcrypt, $ciphertext );

      // destruction de la ressource
      mcrypt_generic_deinit( $this->mcrypt );

      // on retourne le texte
      return $out;
    }

Pour finir la classe, nous avons besoin d’une fonction de destruction qui détruira la ressource mcrypt et qui réécrira en mémoire la clé secrète et substituant chaque caractère par un X.


public function __destruct() {
      // write over key in memory
      $this->key = str_repeat( 'X', strlen( $this->key ) );

      // free the mcrypt resource
      mcrypt_module_close( $this->mcrypt );
    }

    // end of mcrypt class
  }

Ajouter mon fil RSS à votre agrégateur de contenu préféré.

Vous allez me dire qu’il est facile de faire un tri sur un tableau associatif en utilisant une fonction simple comme sort, usortou bien array_ multisort et je suis entièrement en accord avec vous. Par contre, je cherchais une fonction pour trier plusieurs tableaux qui ont été regroupé sous le même tableau.

Voici un exemple un peu plus concret. Vous avez trois tables SQL, une de chiens, une de chats et une d’oiseaux. Vous voulez créer une page qui affiche les derniers animaux ajoutés sur votre site web trié par date d’ajout. Il serait surement possible de le faire en SQL mais j’ai décidé aujourd’hui de le faire en PHP.

J’ai fait des recherches sur les algorithmes de tri. J’en suis venu à la conclusion d’utiliser le Tri par Insertion. Je suis partie du code disponible sur Wikipédia pour en venir avec le code suivant :


function insert_sort($arr,$key,$desc=false){
    $count = count($arr);

	for($i=1; $i< $count; $i++){
        $tmp = $arr[$i];
        $j = $i - 1;
		$done = false;
		while(!$done){
            if($j >= 0){
				if($arr[$j][$key] > $tmp[$key]){
	            	$arr[$j+1] = $arr[$j];
		            $arr[$j] = $tmp;
		            $j--;
				}else{
					$done = true;
				}
			}else{
				$done = true;
			}
         }
     }
	if(!$desc){
		return $arr;
	}
	return array_reverse($arr);
}

Quelques petites explications. En argument on retrouve deux éléments obligatoire ainsi qu’un facultatif. Le premier est le tableaux associatif sur lequel on veut faire le tri. Le deuxième argument est la clé associative sur laquelle le tri est fait (par exemple une date). Le troisième est si le tri est ascendant (false) ou bien descendant (true).


//on crée les tableaux
$array[] = array('nom' => 'Labrador', 'ordre'=> 3);
$array[] = array('nom' => 'Boxer', 'ordre'=> 7);
$array[] = array('nom' => 'Bengal', 'ordre'=> 3);
$array[] = array('nom' => 'Pixie Bob', 'ordre'=> 2);
$array[] = array('nom' => 'Hirondelle', 'ordre'=> 1);
$array[] = array('nom' => 'Cardinal', 'ordre'=> 8);

/*
print_r($array);
Array
(
    [0] => Array
        (
            [nom] => Labrador
            [ordre] => 3
        )
    [1] => Array
        (
            [nom] => Boxer
            [ordre] => 7
        )
    [2] => Array
        (
            [nom] => Bengal
            [ordre] => 3
        )
    [3] => Array
        (
            [nom] => Pixie Bob
            [ordre] => 2
        )
    [4] => Array
        (
            [nom] => Hirondelle
            [ordre] => 1
        )
    [5] => Array
        (
            [nom] => Cardinal
            [ordre] => 8
        )
)
*/
//exécution du tri
$array = insert_sort($array,'ordre');
/*
Deuxième print_r du tableau, mais cette fois il est ordonné
Array
(
    [0] => Array
        (
            [nom] => Hirondelle
            [ordre] => 1
        )
    [1] => Array
        (
            [nom] => Pixie Bob
            [ordre] => 2
        )
    [2] => Array
        (
            [nom] => Labrador
            [ordre] => 3
        )
    [3] => Array
        (
            [nom] => Bengal
            [ordre] => 3
        )
    [4] => Array
        (
            [nom] => Boxer
            [ordre] => 7
        )
    [5] => Array
        (
            [nom] => Cardinal
            [ordre] => 8
        )
)
*/

Pour avoir une programmation plus clair et efficace il est recommandé d’utiliser la programmation orienté objets. L’avantage est d’éviter le dédoublement de code, mais surtout de ce facilité la vie lorsqu’un changement drastique survient à notre code. Comme dans le cas d’une base de données.

La base de donnée est la pièce maitresse d’un gros site web. La plus souvent utilisé avec le PHP est MySQL, mais il existe d’autre type de base de donnée comme par exemple PostGre SQL ou bien SQL Lite. Le choix d’une BD est habituellement effectué avant même le début de développement. Mais il arrive que l’on doit changer le type de BD rapidement en plein milieu du projet, d’où l’importance de centraliser les actions à la BD dans une classe. Voici donc la classe de BD que j’utilise pour mes sites web avec quelques explications. Le code de base est tiré du livre Advanced PHP Programming.

Pour ceux qui ne sont pas à l’aise avec la POO, je vous réfère pour le moment sur un tutoriel du SiteDuZero

On commence tout d’abord a créer les variables de notre classe.


class DB_Mysql{
 protected $user; //utilisateur de la bd
 protected $pass; //mot de passe de la bd
 protected $dbhost; //adresse de la bd, souvent localhost
 protected $dbname; //nom de la db
 protected $dbh; //Connection handle
 protected $cpt_query=0; //compteur de requête
}

Un petit peu d’explication sur le code. Ces variables seront accessibles par le mot clé $this dans toutes les fonctions. Le mot clé protected signifie que la variable sera accessible par toutes les classes en liens avec la classe DB_Mysql de l’intérieur seulement.

Ensuite nous devons utiliser une petit classe qui contiendra notre informations de BD.


class DB_Mysql_Home extends DB_Mysql {
 protected $user   = 'root';
 protected $pass   = '';
 protected $dbname = 'mabd';
 protected $dbhost = 'localhost';

public function __construct() { }

}

La classe DB_Mysql étant une extension de la classe DB_Mysql_Home se retrouve automatiquement avec les informations entrée dans les variable profitant ainsi que la relation parent-enfant de la classe.

Nous créons ensuite la fonction pour la connexion à la bd.


private function connect()
	{
		$this->dbh = @mysql_connect($this->dbhost,$this->user,$this->pass);
		//si la connexion a échoué
		if(!is_resource($this->dbh)) {
			exit('Impossible de se connecter');
		}
		//si la sélection de table a échoucé
		$ret = mysql_select_db($this->dbname,$this->dbh);
		if(!$ret) {
			exit('Impossible de sélectionner la table sql');
		}
	}

Fonction relativement simple. Ce n’est que des fonctions de Mysql très simpliste. La prochain fonction est la plus importante de la classe, soit celle pour éxécuter des requêtes.


public function execute($query)
	{
		if(!$this->dbh) {
			$this->connect();
		}
		$ret = mysql_query($query, $this->dbh);
		$this->cpt_query++;
		if(!$ret) {
			exit('Erreur de requête : '.mysql_error());
		}
		else if(!is_resource($ret)) {
			return TRUE;
		} else {
			$stmt = new DB_MysqlStatement($this->dbh, $query);
			$stmt->result = $ret;
			return $stmt;
		}
	}

Quelques explications. Tout d’abord on vérifie si la connexion existe, si ce n’est pas le cas, nous la créons. Cela évite de créer la connexion si aucune requête ne sera exécuter dans la page. Ensuite nous effectuons la requête grâce à la fonction mysql_query qui nous retourne une ressource si tout c’est bien passé. Si notre variable n’est pas valide nous quittons le programme, sinon la variable doit être une ressource.

Si tout est exécuter sans problème, on peut donc instancier notre deuxième classe qui elle gère les ressource SQL. Pourquoi une deuxième classe, parce qu’elle nous permettra de continuer à travailler en objet. Donc nous envoyons à DB_MysqlStatement le handle de la connexion ainsi que la ressource de la requête et nous retournons ensuite l’objet de la classe.

Ne reste plus ensuite qu’appeler les fonctions nécessaire pour extraire les données de l’objet SQL. Voici le code de le dernière classe.


class DB_MysqlStatement{
	public $result;
	public $query;
	public $dbh;
	public $binds;

	public function __construct($dbh, $query)
	{
		$this->query = $query;
		$this->dbh = $dbh;
		if(!is_resource($dbh)) {
			exit("N'est pas une connexion valide");
		}
	}

	public function fetch_row()
	{
		return mysql_fetch_row($this->result);
	}

	public function fetch_assoc()
	{
		if(!$this->result) {
			exit("La requête n'a pas été exécuté");
		}
		return mysql_fetch_assoc($this->result);
	}

	public function data_seek($pointeur){
		return mysql_data_seek($this->result,$pointeur);
	}

	public function fetch_fetchall_assoc()
	{
		$retval = array();
		while($row = $this->fetch_assoc()) {
			$retval[] = $row;
		}
		return $retval;
	}

	public function num_rows(){
		return mysql_num_rows($this->result);
	}

	public function fetch_array(){
		return mysql_fetch_array($this->result);
	}

	public function fetch_object(){
		return mysql_fetch_object($this->result);
	}

}

Tout d’abord, le constructeur déclenché lors de l’instanciation de la classe lors de l’exécution de la requête. La fonction est relativement simple alors qu’il n’y a qu’une simple validation pour s’assurer que la connexion est valide.
Ensuite on retrouve les fonctions de base d’un driver de base de donnée comme Mysql. Il y a les fonctions pour recevoir les informations de la requête comme fetch_row, fetch_assoc ou bien fetch_array. Ensuite des fonctions utile comme data_seek qui permet de déplacer le pointeur dans un objet de requête ou bien num_rows qui compte le nombre de résultats dans votre requête.

Les différences entre les fonctions de mysql ou autre système de base de données sont minimes mais tellement importante à connaitre. La plupart des programmeurs php lros de leur début utilisent la fonction mysql_fetch_array sans vraiment connaître sont impacte. Les deux autres fonctions semblables sont mysql_fetch_assoc, mysql_fetch_object et mysql_fetch_row. La première fonction (mysql_fetch_array) retourne un tableau avec des clés numérique et associatives, tandis que la fonction mysql_fetch_assoc retourne des clés associatives seulement alors que mysql_fetch_row fait l’inverse. Autrement dit, quand vous utilisez mysql_fetch_array, vous utilise des tableaux deux fois plus gros, qui vous duplique chaque donnée. Voici un exemple de ce que donne un tableau retourner par mysql_fetch_array:

Array
(
[id] => 1
[0] => 1
[nom] => Pierre-Luc
[1] => Pierre-Luc
)

En voici un autre retourner par mysql_fetch_assoc

Array
(
[id] => 1
[nom] => Pierre-Luc
)

Pour conclure sur le code de la classe, elle laisse place a encore beaucoup d’amélioration. On pourrait ajouté une fonction qui prépare chaque variable avant de l’inclure dans la requête. Cela permettrait d’analyser les variables pour éviter les injections SQL. Si vous avez des questions, n’hésitez pas!


© 2008 Blogue de Pierre-Luc Babin | Curved Wordpress Theme by JustSkins.com | Propulsé by Wordpress 2.5