Yii : Crypter les mots de passes dans votre base de données

Pour ce premier tutorial Yii, nous allons nous intéresser à la sécurité dans une application Web et plus particulièrement ce qui concerne les données personnelles des utilisateurs.

Dans la plupart des applications comportant ce type d’informations (descriptif d’un compte, par exemple), on trouvera un champ contenant le mot de passe de l’utilisateur. Par soucis de simplicité, on sera tenté de stocker ces mots de passes tels quels (lisible en clair). Cela serait sans doute raisonnable si les données ne pouvaient être lues que par l’application qui y accèdent. Or, dans le cas d’une base de données MySQL, un simple phpMyAdmin et les autorisations adéquates permettent d’avoir un accès complet à ces données. Sans compter sur les accès illicites ou des personnes non-autorisées.

Sans rentrer dans les détails, vous aurez compris l’utilité de sécuriser autant que possible ce genre de données et en particulier les mots de passes.

Nous allons donc utiliser les fonctions de sécurité de Yii pour créer des données cryptées qui seront quasiment impossible à lire hors de l’application qui les utilisent.
 

1. Le paramétrage

 
Comme bien souvent avec Yii, la première étape consiste a paramétrer le comportement du composant que nous désirons utiliser.

En l’occurrence, nous allons utiliser les méthodes de la classe CSecurityManager. L’utilité principale de cette classe est de protéger les données contre un accès illicite (utilisé en interne par Yii pour la protection des données de cookie par exemple).

Pour ajouter ce composant et le paramétrer dans notre application, nous allons éditer le fichier protected/config/main.php (à la racine du projet). Cela va ressembler à quelque chose comme :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php
 
// uncomment the following to define a path alias
// Yii::setPathOfAlias('local','path/to/local-folder');
 
// This is the main Web application configuration. Any writable
// CWebApplication properties can be configured here.
return array(
	'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
	'name'=>'My Web Application',
 
	// preloading 'log' component
	'preload'=>array('log'),
 
	// autoloading model and component classes
	'import'=>array(
		'application.models.*',
		'application.components.*',
	),
 
	// application components
	'components'=>array(
		'log'=>array(
			'class'=>'CLogRouter',
			'routes'=>array(
				array(
					'class'=>'CFileLogRoute',
					'levels'=>'error, warning',
				),
			),
		),
		'user'=>array(
			// enable cookie-based authentication
			'allowAutoLogin'=>true,
		),
		'db'=>array(
			'class'=>'CDbConnection',
                        'connectionString'=>'mysql:host=localhost;dbname=maBaseDeDonnees',
                        'charset'=>'UTF8',
                        'username'=>'monUserMySQL',
                        'password'=>'monMotDePasseMySQL',
		),
		'securityManager'=>array(
			'encryptionKey' => 'maCleDeCryptage',
			'validationKey' => 'maCleDeValidation'
		)
	),
 
	// application-level parameters that can be accessed
	// using Yii::app()->params['paramName']
	'params'=>array(
		// this is used in contact page
		'adminEmail'=>'webmaster@example.com',
	),
);
?>

Ce qui nous intéresse se trouve entre les lignes 43 et 46. Nous indiquons à l’application que nous allons utiliser la classe CSecurityManager en lui appliquant deux paramètres :

  • encryptionKey : il s’agit d’une clé privée (de votre choix) qui va permettre à l’algorithme de cryptage de produire des résultats spécifiques à votre application
  • validationKey : il s’agit d’une autre clé privée (de votre choix également) qui va servir comme un marqueur dans les chaînes cryptées afin de garantir, en gros, que la dite chaîne provient bien de l’application qui l’a produite

Nous avons besoin de fournir ces paramètres car, dans le cas contraire, la classe produirait les chaînes aléatoires pour nourrir les variables.

2. La table user

Rien de bien particulier à dire concernant cette table de notre base de données. Elle va contenir tous les champs relatifs à un utilisateur donné. Par exemple :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  `login` varchar(80) character set utf8 collate utf8_bin NOT NULL,
  `activated` tinyint(1) NOT NULL default '0',
  `email` varchar(255) NOT NULL,
  `password` varbinary(80) NOT NULL,
  `first_name` varchar(40) NOT NULL,
  `last_name` varchar(40) NOT NULL,
  `level` enum('admin','user') NOT NULL,
  `address` text,
  `postal_code` varchar(20) default NULL,
  `city` varchar(80) default NULL,
  `country_iso` char(2) default NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `login` (`login`),
  KEY `level` (`level`),
  KEY `country_iso` (`country_iso`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

3. Crypter le mot de passe

Je vous laisse vous référer au tutorial officiel concernant la génération des fichiers de modéles PHP dans Yii.
Une fois le modèle de notre table user généré, nous allons le modifier afin qu’il prenne en charge le cryptage automatique du champ password.

Et pour ce faire, nous allons sur-définir la méthode beforeSave de la classe CActiveRecord.

A la fin du fichier protected/models/User.php nous ajoutons ces lignes :

protected function beforeSave()
{
	if(isset($this->password)) $this->password = convert_uuencode(Yii::app()->getSecurityManager()->encrypt($this->password));
	return true;
}

Cela impose quelques explications :

Tout d’abord, le choix de la méthode beforeSave plutôt que beforeValidate permet de garantir que la validation du champ se fera sur le mot de passe tel qu’il a été saisir dans le formulaire et pas tel qu’il sera crypté.

Ensuite, nous ne voulons traiter que les cas ou le champs password est présent. C’est le rôle de la condition if isset.

Le cryptage est assuré par l’appel à la méthode encrypt de la classe CSecurityManager à laquelle on accède par la fonction Yii::app()->getSecurityManager(). La méthode en question produit une chaîne cryptée selon les paramètres définis plus haut dans la configuration de l’application. Attention, car cette chaîne est dite binaire. C’est à dire qu’elle peut contenir des informations qui ne seront pas forcément « affichables » et surtout transmissibles facilement vers un champ de notre base de données. On utilise donc la fonction standard PHP convert_uuencode afin de transcoder notre chaîne binaire en chaîne beaucoup plus classique qui permettra son stockage en base.

C’est tout pour le cryptage. Maintenant, dés que nous appellerons la méthode save de notre modèle et si l’attribut password est défini, il sera automatiquement crypté et stocké.

4. Processus d’identification

Dans Yii, le processus responsable de l’identification d’un utilisateur est contenu dans le fichier protected/components/UserIdentity.php dont voici le code, adapté à notre méthode de crytage :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
/**
 * UserIdentity represents the data needed to identity a user.
 * It contains the authentication method that checks if the provided
 * data can identity the user.
 */
class UserIdentity extends CUserIdentity
{
	/**
	 * Authenticates a user.
	 * @return boolean whether authentication succeeds.
	 */
	public function authenticate()
	{
		$record=User::model()->findByAttributes(array('login'=>$this->username));
        if($record===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if(Yii::app()->getSecurityManager()->decrypt(convert_uudecode($record->password))!==$this->password)
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {
            $this->errorCode=self::ERROR_NONE;
        }
        return !$this->errorCode;
	}
}
 
?>

Ligne 18, on procède exactement à l’inverse du processus de cryptage :

Tout d’abord, on décode la chaîne récupérée de la base de données en lui appliquant la fonction PHP convert_uudecode qui va reconstituer la chaîne binaire originale.

Puis, on y applique la méthode decrypt du Security Manager afin de retrouver le mot de passe original et le comparer au mot de passe saisi par l’utilisateur afin de s’identifier. On notera la grande différence avec une méthode de cryptage classique : On est capable de décrypter le mot de passe afin de retrouver la chaîne d’origine contrairement à d’autres méthodes (comme l’utilisation de l’algorithme MD5) ou le processus est unidirectionnel. Cela permet, par exemple, d’offrir à l’utilisateur un moyen de récupérer son mot de passe par email dans l’éventualité de son oubli.

Conclusion

Voici, en quelques lignes de code, une manière rapide de sécuriser vos données contre certains accès illicites.
Je vous invite à consulter la documentation officielle de Yii relative à la sécurité pour plus d’informations.

Pour les plus paranoïaques d’entre vous, je vous laisse également réfléchir aux améliorations éventuelles à apporter et aux limites du systéme ;) . N’hésitez pas à me faire part de vos questions et réflexions.
A très bientôt pour un nouveau tutorial.

  1. niceboy
    14/12/2010 à 13:06 | #1

    Merci pour le partage.
    j’ai une petite question: quel est l’algorithme utilisé en interne par Yii(md5, sha1,sha255)?

  2. 16/12/2010 à 19:17 | #2

    Bonjour et merci pour votre message.
    Sauf erreur, Yii utilise la librairie PHP Mcrypt afin d’assurer les cryptages / décrypatages via le module SecurityManager. Vous pouvez paramétrer le type d’algorithme à utiliser via le module depuis la version 1.1.3. Voir ici pour plus de détails : http://www.yiiframework.com/doc/api/1.1/CSecurityManager/

  1. 08/02/2009 à 20:23 | #1

Performance Optimization WordPress Plugins by W3 EDGE

Switch to our mobile site