Magazine Entreprendre

Comment éviter le vol d’identité dans vos applications Zend Framework avec le composant Zend_Auth

Publié le 20 janvier 2011 par Fred Blanc

Sécurisé les applications Zend Framework avec Zend_Auth

Note du traducteur : Cet article est une traduction de l’article PHP Tutorials, écrit par Andrei Gabreanu. Je le remercie au passage de m’avoir autorisé à effectuer cette traduction et à la publier sur mon blog.

Si vous constatez des typos ou des erreurs de traduction merci de m’en faire part.

Introduction : Le tutoriel suivant va vous montrer comment augmenter l’ensemble de la sécurité de votre application PHP (ce tutoriel est basé sur le Zend Framework 1.10 et sur l’utilisation de Zend_Auth mais vous pouvez facilement l’adapter à vos propres besoins). Vous allez apprendre comment étendre la classe Zend_Auth en une classe d’authentification hautement sécurisée et adaptable.

Lorsque je construis mes applications, j’essaye toujours d’améliorer le code que j’écris de multiple manières. Aujourd’hui, j’ai pensé à des failles de sécurité de n’importe quelle application PHP utilisant un système d’authentification.

Les failles de sécurité des applications Web

Les principales questions de sécurité au sein d’applications web sont les suivantes :

  • Cross-site scripting (voir Wikipedia – Cross-Site Scripting)
  • Paramètres invalides
  • Faille dans le contrôle d’accès
  • Problème de gestion des erreurs
  • Usage non sécurisé de la cryptographie
  • mauvaise configuration dans le serveur web et applicatif
  • compte cassé et gestion de session

Même si ce sont tous des problèmes majeurs, il y en a un en particulier qui m’agace depuis un certain temps. Le vol d’identité – compte cassé et problème de gestion de session.

Pourquoi peut-on si facilement conserver mon identifiant de cookie de session et tout à coup avoir accès à mon compte dans une application web en particulier ? Je sais qu’il est quasiment impossible d’être 100% protégé aux tentatives de piratage (hack) mais je suis persuadé que le système devrait être amélioré autant que possible.

Dans les quelques lignes suivantes, je vais vous montrer comment vous pouvez faire cela.

Notre but

Notre objectif est de mettre en œuvre une extension Zend_Auth qui ajoute un nouveau niveau de sécurité à cette classe.

Cette extension – que nous appellerons Project_Application_Auth – vérifiera le stockage Zend_Auth pour les propriétés IP et/ou du User Agent.

Pour ce faire, elles doivent être stockées lors du processus de connexion.

Si l’IP est différente de celle fournie lors de la phase de login et/ou si le User Agent de l’utilisateur est différent de celui mis en cache lors de la phase, notre extension nous avertira que ce n’est pas une identité sécurisée (comprendre : il est recommandé de supposer qu’il a été volé) et nous devrions donc déconnecter l’utilisateur.

Les raisons d’utiliser ces méthodes

Je suis venu à cette idée depuis que je me suis demandé :  Existe t-il vraiment un cas où je pourrai me retrouver avec le même ID de session, mais avec une IP différente ou un autre navigateur?

La réponse est :  oui. Il y a une chance avec le premier des deux cas. Si j’ai une IP dynamique, elle pourrait changer sans que je le sache, ce qui m’amènerait à un état non authentifié.

Mais en réalité, cela arrive rarement (même si votre IP change, elle ne changera pas plus d’une fois par semaine, non ?).

Vous pourriez vous demander pourquoi l’extension que je suis sur le point de vous montrer, vérifie également l’adresse IP quand on peut simplement vérifier le User Agent (puisque ce ne peut certainement pas être le même sur une IP différente, car le cookie de session id ne peut exister que dans un seul navigateur, dans aucun autre).

La réponse : parce que tout pirate qui a volé votre identité pourrait bien utiliser le même navigateur que vous utilisiez et ainsi contourner complètement nos protections.

Et comme dernière raison, oui, si le pirate a la même adresse IP que la vôtre, vole votre identité (id de cookie de session) et utilise le même navigateur, alors il contournera le système. Mais bon, nous pouvons au moins rendre cette tâche plus difficile pour lui !

Le projet Application_Auth_Extension

La classe suivante doit être placée dans votre /chemin/vers/le/projet/……/library/Project/Application/Auth.php

class Project_Application_Auth extends Zend_Auth
{
/**
* Singleton instance
*
* @var Project_Application_Auth
*/
protected static $_instance = null;
/**
* Defines how much time to wait until
* to reinitialize the session id
*/
protected static $_session_exp_time = 5;
/**
* Defines if to validate a secure identity
*/
protected static $_secure = TRUE;
/**
* Defines secure identity check level
*
* 1 - Check only IP
* 2 - Check only UserAgent
* 3 - Check IP & UserAgent
*/
protected static $_secure_level = 3;
/**
* Returns an instance of Project_Application_Auth
*
* Singleton pattern implementation
*
* @return Project_Application_Auth Provides a fluent interface
*/
public static function getInstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Sets wheter the method @see hasSecureIdentity
* to work or not. If set to FALSE then this extension will work
* as the normal Zend_Auth class
*
* @param boolean true
*/
public function setSecure($status = TRUE)
{
if ($status === TRUE)
{
self::$_secure = TRUE;
}
if ($status === FALSE)
{
self::$_secure = FALSE;
}
return TRUE;
}
/**
* Sets the level of security for the @see hasSecureIdentity method
*
* @param integer $level (can be 1 or 2 or 3)
*/
public function setSecureLevel($level)
{
if (in_array($level, array(1, 2, 3)))
{
self::$_secure_level = $level;
}
return TRUE;
}
/**
* Returns true if and only if an identity is not stolen
*
* Checks if IP and/or User Agent (@see $_secure_level)
* match the initial authentication data
*
* @return boolean
*/
public function hasSecureIdentity()
{
if (self::$_secure === FALSE)
{
return TRUE;
}
if (FALSE == $this->getStorage()->isEmpty())
{
$storage = $this->getStorage()->read();
if (self::$_secure_level == 3)
{
return $storage->ip == $_SERVER['REMOTE_ADDR']
& $storage->user_agent == $_SERVER['HTTP_USER_AGENT'];
}
elseif (self::$_secure_level == 2)
{
return $storage->user_agent == $_SERVER['HTTP_USER_AGENT'];
}
elseif (self::$_secure_level == 1)
{
return $storage->ip == $_SERVER['REMOTE_ADDR'];
}
else
{
return FALSE;
}
}
else
{
return FALSE;
}
}
/**
* If the Zend Auth Storage
* has been initialized and is a Session Storage
* and the last time it has been reinitialized is bigger then
* the @see $session_exp_time then reinitialize the session id
*
* @return boolean
*/
public function reinitSecurity()
{
if (isset($_SESSION['Zend_Auth']))
{
$zend_auth_session_namespace = new Zend_Session_Namespace('Zend_Auth');
if (!isset($zend_auth_session_namespace->initialized)
|| $zend_auth_session_namespace->initialized + self::$session_exp_time < time() ) {
Zend_Session::regenerateId();
$zend_auth_session_namespace->initialized = time();
}
}
return TRUE;
}
}

Maintenant, arrêtons-nous une seconde pour comprendre ce que ces inquiétantes lignes de code font :
Tout d’abord, l’extension implémente le pattern singleton. Ensuite, nous avons 3 propriétés:

La première, $_secure, nous permet d’activer et de désactiver l’ensemble de la classe, nous laissant auquel cas avec le système habituel Zend_Auth.

La seconde, $_secure_level, définit le niveau de vérification : soit juste l’IP, soit uniquement le User Agent ou encore les deux à la fois.

La troisième, définit $_session_exp_time défini quand doit être réinitialisé le Zend Auth Session Storage Id (aka PHPSESSID id) si la méthode reinitSecurity est appelée.

Comment utiliser cette classe

Dans votre contrôleur de base – que nous appellerons Project_Application_Controller (que chaque contrôleur devraient étendre) – vous devez avoir le code suivant :

abstract Class Project_Application_Controller extends Zend_Controller_Action
/**
* Initialize the application controller
*/
public function init()
{
parent::init();
$this->_initSession();
debug($_SESSION);
}
/**
* Inits the User Session
* and refresh the session id
*/
protected function _initSession()
{
Project_Application_Auth::getInstance()->reinitSecurity();
}

Ce principe nous permet simplement d’exécuter la méthode reinitSecurity à chaque exécution de l’application, tout en nous permettant d’initialiser entièrement le Front Controller.

Maintenant, dans votre méthode preDispatch (), vous pourriez avoir quelque chose comme ceci:

if ( FALSE === Project_Application_Auth::getInstance()->hasIdentity() { //do stuff here } else { //other stuff here }

Cela contrôle simplement si l’utilisateur a été authentifié précédemment. Juste en-dessous, ajoutez le code suivant:

if ( FALSE === Project_Application_Auth::getInstance()->hasSecureIdentity()
& 'users' !== $this->getRequest()->getControllerName()
& 'login' !== $this->getRequest()->getActionName()
& 'error' !== $this->getRequest()->getControllerName())
{
Zend_Auth::getInstance()->clearIdentity();
// redirect to login page
}

Je suppose ici que vous avez un contrôleur nommé ‘Users’ avec une action ‘login’ (vous pouvez changer ces noms comme vous le souhaitez).

Enfin, dans votre processus de connexion (login), partout où vous exécutez votre méthode Zend_Auth authenticate(), ajoutez le code suivant :

$auth = Project_Application_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid())
{
$storage = $authAdapter->getResultRowObject();
$storage->ip = $_SERVER['REMOTE_ADDR'];
$storage->user_agent = $_SERVER['HTTP_USER_AGENT'];
$storage = $auth->getStorage()->write($storage);
return TRUE;
}
else
{
return FALSE;
}

Cela va placer votre objet User dans Zend_Auth storage associé à son IP et son User Agent.

Conclusion

J’espère que vous aimerez cette extension. Je suis vraiment intéressé par vos réflexions sur ce système et vous encourage à laisser vos commentaires ci-dessous. J’y répondrai le plus rapidement possible.

A bientôt.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Fred Blanc 458 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte

Dossiers Paperblog