dimanche 9 novembre 2008

Contrôler le temps d'execution maximum d'un traitement PHP

Il est parfois nécessaire d'exécuter un traitement PHP avec une contrainte de temps maximum à ne pas dépasser. Dans cette article je vais vous proposer une solution à ce problème.

Voici un petit exemple impossible que nous allons tenter d'implémenter : "Faites ce que vous voulez dans une boucle infinie. Si au bout de 5 secondes vous n'avez pas terminé, stoppez le traitement".

Voici une classe qui va s'occuper de ce travail :

class   MissionImpossible
{
  public function start()
  {
    while (42)
      {
        echo "processing nothing...\n";
        sleep(1);
      }
  }

  public function abort()
  {
    throw new Exception("Time limit ! job aborted");
  }
}

Maintenant, passons aux choses intéressantes : comment s'y prendre pour appeler la fonction abort() au bout de 5 secondes, alors que la fonction start() est en train de s'executer ?

Nous allons utiliser le mécanisme de signaux POSIX pour interrompre le processus et l'informer que le temps qui lui était alloué arrive à terme. Le signal SIGALRM est parfaitement adapté à ce cas d'utilisation.

Deux fonctions vont nous être utiles en PHP :

  • pcntl_signal qui permet de definir quelle fonction va etre appelée lorsqu'un signal particulier est reçu.
  • pcntl_alarm qui permet de mettre en place un compte a rebours, qui lorsqu'il est terminé envoie un signal SIGALRM au processus courant.

Voici donc comment résoudre notre problème :
declare(ticks = 1);
require('MissionImpossible.php');

$job = new MissionImpossible();
pcntl_signal(SIGALRM, array($job, "abort"), true);
pcntl_alarm(5);
try {
  $job->start();
}
catch (Exception $e)
{
  echo "Exception caught. Stopping execution\n";
}

Quelques commentaires :

  • Lignes 1 et 2 : l'appel à la fonction declare est indispensable pour que le mécanisme de signaux puisse fonctionner en PHP. Il doit être fait impérativement avant la déclaration de la classe MissionImpossible.
  • Ligne 5 : mise en place du gestionnaire de signaux : la fonction abort() de l'instance $job de la classe MissionImpossible sera appelée dès qu'un signal SIGALRM sera reçu
  • Ligne 6 : mise en place du compte à rebours. Dans 5 secondes, un signal SIGALRM sera envoyé au processus courant
  • Dans la fonction abort(), une exception est lancée. Sans elle, le traitement de la fonction start() aurait continué dès la fin de la fonction abort().

Voilà, c'est tout simple. Mais sachez que les fonctions de l'extension pcntl ont quelques inconvénients : (cf doc officielle)

  • "Cette extension ne doit pas être activée pour une utilisation en serveur web, car les résultats pourraient être inattendus"
  • "Cette extension n'est pas disponible sur les plates-formes Windows."
Si cela vous pose problème, regardez du coté de register_tick_function (qui a lui aussi quelques inconvenients : " [...] ne doit pas être utilisé avec les modules de serveur web threadé. Les ticks ne fonctionnent pas en mode ZTS et peuvent interrompre votre serveur web.")

Enfin, cet exemple était vraiment simpliste mais sachez que c'est applicable pour tout traitement lourd et "réversible" (par exemple une grosse transaction MySQL).

vendredi 26 septembre 2008

Parlons d'ergonomie

L'une des règles d'ergonomie à appliquer lorsque l'on fait un site web est d'utiliser un vocabulaire standard pour que les visiteurs ne soient pas perdus. Mais... comment choisir correctement ses mots ?

La méthode classique : constituer une liste de mots "éligibles", parcourir le web pour trouver lesquels sont les plus populaires, puis enfin faire son choix. Au fur et à mesure, on accumule de l'expérience et se fait son propre dictionnaire de mots à utiliser.

Ne serait-il pas intéressant de cumuler nos expériences sur un site qui servirait de dictionnaire de référence en la matière ? Fonctionnant de la même manière qu'un dictionnaire de synonymes, il pourrait pour chaque mot donner les alternatives possibles et leur popularité, ainsi qu'une liste de sites qui les utilise. Ce serait un gain de temps pour nous tous.

Exemple:

Deconnexion
Utilisation recommandée
Alternatives: Quitter, Va-voir-ailleurs-si-j'y-suis!

Si ce service existe déjà, merci de me donner l'URL ;-) Sinon je trouve que ce serait un bon projet... Je n'ai malheureusement pas le temps de me lancer dessus. Si ça tente quelqu'un, l'idée attend au chaud !

samedi 5 juillet 2008

CSRF : Sea, Surf and Zend

Note: Si vous n'utilisez pas Zend Framework et que le titre vous a fait peur, ne fuyez pas, je ne parle de ZF qu'à la fin.

Au programme d'aujourd'hui, un peu de prévention ! Comme vous l'avez remarqué, l'été pointe enfin le bout de son nez et il devient difficile de rester concentré devant son écran. On parle souvent d'insolations, de déshydratations, mais n'oubliez pas non plus qu'une seule seconde d'inattention peut mener à une faille de sécurité dans votre code !

C'est pourquoi j'aimerais vous parler des failles de type CSRF. Si cela ne vous évoque rien ou pas grand chose, je vous conseille d'aller vous renseigner sur le sujet, de nombreux articles expliquent le problème.

Comment s'en protéger ? Tout d'abord je vous conseillerais de boire de l'eau (sans aucun additif anisé) lorsque vous codez. Mais ça ne suffit pas ! Les techniques suivantes sont souvent utilisées pour se protéger contre les CSRF :

  • Utiliser la méthode HTTP POST (ou UPDATE ou DELETE) pour toutes les actions qui ne sont pas de la consultation de ressource (insertion, mise à jour ou suppression). D'une part, vous protégera légèrement contre le CSRF, d'autre part ça sera le premier pas vers une architecture RESTful
  • Utiliser des token de sécurité (aussi appelés nonce ou encore bordel pour paranoïaque) Kézako ? En gros lorsqu'un utilisateur affiche un formulaire, on lui génère une clé. Cette clé sera valide pour un certain temps (5 min en général), sera liée uniquement au couple utilisateur/formulaire et devra automatiquement être transmise avec le formulaire pour que celui ci puisse être validé !

En général, cela évite les dégats. Par contre méfiez vous car si vous êtes également vulnérable à une faille de type XSS, ces protections ne changeront absolument rien ! Ce serait aussi efficace que d'installer une porte blindée à la place de la fermeture d'une tente de camping en toile. Un simple cure dent permettrait de désintégrer votre site web.

Donc techniquement, pour se protéger, il faut agir à deux niveaux :

  1. Affichage du formulaire
    • Générer un token valide pour cet utilisateur et ce formulaire [1]
    • Stocker ce token quelque part [2]
    • Ajouter la valeur du token dans un champ caché du formulaire
  2. Validation du formulaire
    • Ne traiter que les données qui ont ete envoyées par POST (ou UPDATE ou DELETE)
    • Récupérer le token valide quelque part [2] et vérifier qu'il n'est pas expiré
    • Comparer ce token à celui qui a été POSTé

Simple, non ? Étudions maintenant deux points :

  1. Le token ne doit pas être prédictible. Je vous propose d'utiliser la formule suivante :

    MD5( identifiant_formulaire . server_secret . assaisonnement)
    identifiant_formulaire est une chaîne représentant le formulaire concerné
    server_secret est une clé connue uniquement par le serveur
    assaisonnement est un nombre aléatoire (entre 1 et beaucoup, par exemple)

  2. Tout est possible, tout est imaginable...
    • Sessions natives PHP
    • Memcache
    • Base de données (MySQL, Postgresql, sqlite, Berkeley DB, Memcachedb)
    • Cookie sécurisé (c'était juste pour les citer mais je vous les déconseille pour cette utilisation. Si vous avez beaucoup de formulaires ça va créer beaucoup de données qui transiteront entre l'utilisateur et le serveur à chaque requête)
    ... à condition bien sur que personne ne puisse altérer les données que vous y avez stocké.

Si sauvegarder les token en session vous convient, le Zend Framework propose une solution qui fait exactement tout ça : Zend_Form_Element_Hash.

Moi je n'aime toujours pas les sessions, donc je vais préférer Memcache pour y stocker les token. Memcache fournit nativement le système d'expiration, c'est rapide, et c'est pratique a utiliser dans un environnement multi-serveur. Note: Il y a un risque que certains token soient perdu, compte tenu du mode de fonctionnement de Memcache. Mais je vais prendre ce risque compte tenu de leur faible durée de vie. De plus, je vais surveiller l'utilisation de mémoire de memcache pour éviter le problème.

J'ai donc créé une classe qui ressemble beaucoup à Zend_Form_Element_Hash, sauf qu'elle implémente un mécanisme de "Storage Adapter" qui permet de stocker les tokens n'importe où. J'ai également créé un Adapter qui permet de stocker les données dans Memcache, mais libre à vous de créer les Adapter de vos rêves (j'étudie actuellement une solution de stockage des token dans un bigorneaux fossilisé, mais c'est pas très scalable).

C'est ici pour télécharger les classes BigOrNot_Form_Element_Token*

Exemple d'utilisation :

// Initialisation du formulaire
$form = new Zend_Form ...

// Initialisation de Memcache
$memcache = new Memcache ...

// Recuperation de l'identifiant de l'utilisateur
$uniqueUserID = Zend_Auth::getIdentity();

// Mise en place du token de protection
$storage = new BigOrNot_Form_Element_Token_Storage_Memcache($memcache, $uniqueUserID);
$tokenElement = new BigOrNot_Form_Element_Token('formulaire_ajout_bigorneaux', array('adapter' => $storage));
$form->addElement($tokenElement);

Enjoy !

Pour conclure, une petite quote d'un papier (Advanced CRSF) de l'Epitech Security Lab :

Tant que le développement web sera fait par des gens non sensibilisés aux plus évidentes règles de sécurité, que des langages à pièges comme PHP seront “maitrisés” entre deux cours de géographie, il va falloir se faire à l’idée de surfer avec netcat et lire ses mails avec gnus.

Une solution ? Faites circuler cet article dans la boîte au lettre de tous vos voisins (sinon votre serveur Web va perdre ses dents et votre système va mourir d'un kill -9 -1)

;-)

lundi 23 juin 2008

Une classe de protection contre le flood

Il est très courant d'être victime de flood pour certaines fonctionnalités proposées par votre site.

Exemples :

Que penseriez vous d'une classe qui permettrait de bloquer (ou plutôt ralentir) les tentatives de flood sur les fonctionnalités sensibles de votre site ? (ex: pas plus d'1 post sur le forum toutes les 30 secondes).

Pour cela, nous allons utiliser memcache. Si vous ne connaissez pas memcache, c'est un système de cache distribué extrèmement rapide. Comme tout système de cache, il offre un mécanisme de stockage, de récupération et d'expiration des données.

Pourquoi memcache ?
C'est un outil pratique aussi bien dans un environnement multiserveur que sur un seul serveur, donc il est accessible à un grand nombre de personnes (désolé ca n'inclu pas les comptes de type "mutualisés" grand public).
Il propose également un mécanisme d'expiration qui correspond parfaitement à nos besoins pour ce système de flood-control.

Principe :
l'extension memcache pour PHP propose une fonction Memcache::add() qui permet d'ajouter une donnee au cache si et seulement si celle-ci n'existe pas déjà.
A chaque action d'un utilisateur que nous voulons limiter, il suffit donc d'ajouter une information dans le cache avec une date d'expiration correspondant au temps d'attente necessaire avant la prochaine action. Si la fonction add() renvoie une erreur, c'est qu'une action a déjà été effectuée durant cette intervalle de temps.

Scénario type : (flood control sur un forum, 1 post toutes les 30 sec)
11:00:00 : jean-craoude much poste un message sur le forum. Le forum ajoute une entrée au cache qui a pour ID "ForumPost:JeanCraoudeMuch" et pour date d'expiration 30 secondes.
11:00:15 : jean-craoude Much s'endort malencontreusement sur la touche F5 de son clavier. Toutes les nanosecondes, une requête est donc envoyée au serveur pour poster à nouveau le même message. Le forum va donc pour chaque requête tenter de remettre en cache une donnée ayant pour ID "ForumPost:JeanCraoudeMuch", mais celle-ci existant déjà, la fonction Memcache::add() va renvoyer une erreur. Le post sera donc refusé.
11:00:29 : jean-craoude Much se réveille et se regarde dans le mirroir. Voyant un F5 rouge se dessiner sur son front, il se précipite sur le forum pour voir l'etendue de ses dégats. Fort heureusement, tout va bien, il est rassuré. Il se rendort donc consciencieusement, mais cette fois-ci à côté du clavier

Bref, si vous êtes séduit par ce système, voici une petite classe qui vous permettra de le mettre en place très facilement : BigOrNot_FloodControl

Exemple d'utilisation :

/* Initialisation d'un objet memcache */
$cache = new Memcache;
$cache->addServer('127.0.0.1');

/* Initialisation de la classe flood control */
$floodControl = new BigOrNot_FloodControl($cache);

/* ... */

/* Utilisation de la classe pour limiter les posts dans un forum */
$userId = Forum::getUserId();
if ($floodControl->isAllowed('posterUnMessageDansLeForum', $userId, 30))
{
    posterMessage();
}
else
{
    messageErreur();
}

Voila, c'est simple et efficace. Inutile de s'armer d'une base de données pour ce genre de choses.

Notes :

  • Avec cette solution, il n'y a pas la possibilité de faire des restrictions avec une intervalle inférieure à 1 seconde. Pour les amateurs de nanosecondes, cela nécessitera quelques modifications.
  • Je n'ai pas utilisé Zend_Cache car je n'ai pas trouvé de mécanisme équivalent au add() de Memcache. Il y a probablement moyen de mettre en place un système de lock qui y ressemblerait, mais ca ne m'enchantait pas trop... Alors si l'envie vous prend, n'hesitez pas ;) Méfiez vous des appels simultanés à votre système

dimanche 15 juin 2008

Zend_Auth et le cookie masqué

Je vais encore vous parler de cookies, mais cette fois-ci à la sauce Zend_Auth.

Je vous ai présenté dans un précédent article une classe permettant de gérer des cookies sécurisés. Maintenant nous allons voir comment l'utiliser dans un cas très courant : l'authentification avec Zend_Auth.

Rappelons les grandes lignes de Zend_Auth :

Si comme moi vous n'utilisez les sessions que pour stocker l'identité des utilisateurs authentifiés (leur identifiant, un simple nombre entier en général), vous allez peut être trouver que c'est du gachi. Les sessions c'est lourd et ennuyeux à gérer quand l'application en question tourne sur une ferme de serveurs web (qui font meuh meuh en core et en core).

Bref, ne serait-il pas tentant de déléguer cette tâche au client ? Qu'il présente lui même son identité ? C'est risqué vous allez dire... et vous avez raison !
On peut déjà imaginer les pires scénarios :

Serveur> Bien le bonjour ! A qui ai-je l'honneur ?
Client (de type malicieux)> Je suis l'administrateur du site ! Regarde j'ai... ma bonne parole pour te le prouver !
Serveur> Salut chef je t'avais pas reconnu. Voici les clefs de ton royaume.

Techniquement, ca reviendrait a stocker l'user_id dans un cookie et de faire complètement confiance à ce cookie.

En général ce genre de scénarios est très peu apprécié par le public (en particulier celui de votre site). Donc on va tenter d'utiliser quelques ruses cryptographiques pour confier à l'utilisateur le soin de présenter son identité, sans qu'il puisse la modifier : on va la stocker dans un cookie sécurisé grâce à la classe BigOrNot_CookieManager.

J'ai donc écrit une classe qui implémente l'interface Zend_Auth_Storage_Interface pour stocker l'identité des utilisateurs dans un cookie sécurisé.

Vous pouvez la télécharger ici (l'archive contient également la classe BigOrNot_CookieManager).

Voici comment l'utiliser :

$cookieManager = new BigOrNot_CookieManager('SECRET_KEY');
$authStorage = new BigOrNot_Auth_Storage_Cookie($cookieManager);
$auth = Zend_Auth::getInstance();
$auth->setStorage($authStorage);

[...]

Par defaut, le cookie s'appelle "auth" et les paramètres par défaut sont envoyes a setcookie().

Si vous souhaitez modifier ces paramètres, il est possible d'envoyer au constructeur de la classe BigOrNot_Auth_Storage_Cookie un deuxième paramètre : un tableau (ou un objet Zend_Config) de configuration.

Les paramètres de configuration supportés sont : cookieName, cookieExpire, cookiePath, cookieDomain, cookieSecure, cookieHttpOnly. (Les noms sont assez explicites, voir la doc de setcookie() si vous avez des doutes).

Exemple d'utilisation avec configuration :

$cookieManager = new BigOrNot_CookieManager('SECRET_KEY');

$storageConfig = array(
    'cookieName' => 'BigOrNauth',
    'cookieExpire' => (time() + 3600),
    'cookiePath' => '/',
    'cookieDomain' => 'bigornot-fr.blogspot.com'
);

$authStorage = new BigOrNot_Auth_Storage_Cookie($cookieManager, $storageConfig);
$auth = Zend_Auth::getInstance();
$auth->setStorage($authStorage);

L'identité est stockée "serialisée" donc vous pouvez y stocker toute valeur serialisable. Evitez tout de même les gros objets, les cookies trop gros sont vite écoeurants. N'oubliez pas qu'ils sont transmis à chaque requête.

L'identité est également stockée chiffrée, donc vous ne donnez aucune information confidentielle à l'utilisateur en utilisant cette technique.

Pour information :
Comme nous l'avons vu dans le précédent article, pour la methode BigOrNot_CookieManager::setCookie(), il est necessaire d'envoyer en paramètre un nom d'utilisateur (ou n'importe quel identifiant unique).
Dans la classe BigOrNot_Auth_Storage_Cookie, j'envoie un hash md5 de l'identite serialisee.

vendredi 13 juin 2008

Sécurisation des cookies : une implementation en PHP

Note pour les nouveaux arrivants : vous devriez lire avant tout le précedent article : Introduction à la sécurité des cookies.

Donc... où en étions nous ? Il existe un protocole de sécurisation des cookies (voir le papier de Alex X. Liu pour les details) :

Cookie value =
username|expiration time|(data)k|HMAC(username|expiration time|data|SSL session key, k)
Avec
  • username est le nom de l'utilisateur (ou un identifiant unique)
  • expiration time est la data d'expiration du cookie
  • (data)k est le résultat d'une function de chiffrement par bloc (ex: AES) de data avec la clé k.
  • data est la donnée que vous souhaitez stocker dans le cookie
  • k est le résultat de la fonction HMAC(username|expiration time, sk)
  • sk est une clé secrète que seul le serveur connait
  • SSL session key est l'identifiant de la session SSL en cours.

Ce protocole est sécurisé :

  • Il protège vos cookies contre les "replay attacks" : L'identifiant de la session SSL en cours est unique. Si un utilisateur malicieux essaie d'envoyer le cookie d'un autre dans une autre session SSL, le cookie sera invalide.
  • Il protège la confidentialité des données grâce à une fonction de chiffrement symmétrique : les données sont stockées sous une forme chiffrées.
  • Il protège l'intégrité des données grâce à une fonction HMAC
  • Il protège la clé secrète du serveur contre les "volume attack" : le fait d'inclure le nom de l'utilisateur et la date d'expiration dans la clé k permet d'éviter qu'un analyste malicieux s'amuse à créer un grand nombre de cookies sécurités pour essayer de retrouvé la clé secrète stockée sur le serveur.

Cependant, ce protocole n'est pas adapté à tous les usages :

  1. SSL c'est chouette, ça protège contre les replay attacks, mais :
    • Votre application n'est pas forcément accessible en HTTPS
    • La durée de vie du cookie est limitée à celle de la session SSL.
  2. Le chiffrement des données stockées dans le cookie, c'est cool, mais ça ne vous interresse pas forcement ! Si vous n'en avez pas l'utilité, ça va augmenter la taille du cookie pour rien. Par exemple : si les données sont chiffrées avec en AES256, la valeur chiffrée fera au moins 32 octets, même si les données en clair ne font d'1 octet !

Donc il serait cool d'avoir une classe de gestion des cookies qui puisse être configurable, c'est à dire qui offrirait la possibilité de :

  • activer/désactiver le support SSL
  • activer/désactiver les chiffrement des données à stocker
  • choisir l'algorithme de chiffrement

J'ai créé une classe qui offre toutes ces fonctionnalités. Vous pouvez la télécharger ici.

Utilisation :

Initialisation

include('BigOrNot_CookieManager.php');

$secretKey = 'Cei4Wai4ohcoo3daeHooFiek5Nah3Eet';
$manager = new BigOrNot_CookieManager($secretKey);

Tout ce que vous avez à faire, c'est fournir une clé secrète en premier paramètre. Si vous n'êtes pas inspiré, pour générer la clé vous pouvez utiliser cette commande :

pwgen -sy 65

La configuration par défaut est :

  • Chiffrement des données : activé option name : high_confidentiality (bool)
  • Algorithme de chiffrement : MCRYPT_RIJNDAEL_256 (Rijndael 256) option name : mcrypt_algorithm (voir la doc mcrypt)
  • Mode de chiffrement par bloc : MCRYPT_MODE_CBC (CBC) option name : mcrypt_mode (voir la doc mcrypt)
  • Utilisation de l'identifiant de session SSL : disabled option name : enable_ssl (bool)

Si vous souhaitez modifier la configuration, vous pouvez passer en second paramètre du constructeur un tableau d'options. Par exemple, si vous souhaitez désactiver le chiffrement des données, vous pouvez faire cela :

include('BigOrNot_CookieManager.php');

$secretKey = 'Cei4Wai4ohcoo3daeHooFiek5Nah3Eet';
$config = array('high_confidentiality' => false);
$manager = new BigOrNot_CookieManager($secretKey, $config);

Envoyer un cookie sécurisé

$expire = time() + 86400;
$value = $manager->setCookie('cookieName', 'value', 'username');
Regardez le code source si vous souhaitez plus de détails sur les arguments qu'il est possible d'envoyer en paramètre (globalement, vous pouvez envoyer tous les paramètres que setcookie() supporte).

Lire/vérifier la valeur d'un cookie

$value = $manager->getCookieValue('cookieName');
Si le cookie est invalide (utilisation malicieuse, date d'expiration passée), il est supprimé automatiquement. Si vous n'aimez pas ce comportement, vous pouvez le desactiver en envoyant "false" en 2ème paramètre.

Supprimer un cookie

$manager->deleteCookie('cookieName');

N'hésitez pas à regarder le code source pour plus d'infos, il est commenté. Si vous avez des questions, des remarques, des rapports de bugs, n'hésitez pas à poster un commentaire :)

Note: Il existe une classe qui permet de faire à peu près la même chose avec Django (Python) ici. Cependant elle ne gère ni le chiffrement ni SSL.

Note2: La fonctionnalité SSL ne devrait marcher qu'avec le mod_ssl d'Apache (qui créé généreusement une variable d'environnement SSL_SESSION_ID).

Introduction à la sécurité des cookies

J'ai vu beaucoup de gens tenter de créer un protocole de sécurisation des cookies. Nombre d'entre eux sont peu fiables car leur sécurité est basée sur le secret de l'algorithme employé. Ca peut marcher un certain temps, ça peut rassurer psychologiquement, mais au final, c'est pas fiable. Dans un premier temps, pour que ce soit clair, on va définir ce qu'est un cookie "sécurisé" :
  1. Pour certains, c'est un moyen de garantir l'authenticité et l'intégrité des données qui sont stockées dans un cookie. Ils ne veulent pas qu'un utilisateur malicieux puisse modifier la valeur du cookie pour les duper.
  2. Pour d'autres, un cookie sécurisé garantit la confidentialité des données stockées sur la machine du client. C'est à dire que les données ne peuvent être lues/comprises ni par l'utilisateur, ni par une personne malicieuse qui aurait volé un coookie. Seule l'application web (donc coté serveur) à le droit de lire les valeurs stockées dans le cookie.
  3. Pour d'autres, un cookie sécurisé garantit qu'il ne peut être ni intercepté, ni "rejoué" (replay attack) par une personne malicieuse (dans un environnement non sécurisé : machine passeoire, cybercafé, wifi ouvert ou mal sécurisé...etc)
Vous allez peut être me demander : "Mais pourquoi stocker des données sensibles/confidentielles dans des cookies ? Un peu après l'age de la préhistoire on a inventé un mécanisme de sessions pour stocker ce genre de données ! Les cookies, c'est aussi fiable que le réseau Wifi de ma grand mère ! Et elle n'a jamais entendu parler de WPA ou de wardriving de toute sa vie ! T'aimes vraiment jouer avec le feu ?"
Ca fait beaucoup de questions, mais je vais tenter d'y répondre. Pour commencer, oui j'aime jouer avec le feu !

Ensuite, pour cette histoire de cookies, oui il est possible de stocker des données sensibles en session sur le serveur. Mais je n'aime pas les sessions (et je ne suis pas le seul) : c'est une vraie galère à gérer dans un environnement multiserveur. Cela dit, on est pas là pour parler de sessions.

Quoi qu'il arrive, même avec une session, vous aurez à stocker l'identifiant de session dans un cookie. Et ce cookie est TRES IMPORTANT... il doit être sécurisé :) La plupart des applications sont concernées par la sécurité des coookies.

Donc poursuivons : suivant la nature des données que vous aurez à stocker dans des cookies, vous allez devoir considérer les différents points évoqués plus haut (authenticité, integrité, confidentialité, "replay attack" et interception de cookie) et choisir ceux que vous voudrez prendre en compte dans votre politique de sécurité.

Pour chacun des problèmes évoqués, il existe une solution (cryptographique) :
  • Le problème d'intégrité et d'authenticité peut être résolu avec des fonctions de hachage. Nous allons utiliser une solution basée sur ces fonctions de hachage : HMAC.
  • Le problème de confidentialité peut être résolu en utilisant des fonctions de chiffrement par bloc (par exemple AES).
  • Le problème de "replay attack" peut être résolu en combinant l'utilisation d'une fonction HMAC et en utilisant le protocole de transport sécurisé SSL/TLS.
  • Le risque d'interception de cookies peut être réduit en utilisant SSL. (réduit seulement : un cookie peut toujours être volé d'une autre façon car il est stocké de sur la machine du client, il peut donc être volé). Cependant, l'interception d'un cookie sécurisé ne devient plus très interressante si la valeur est chiffrée, non rejouable dans une "replay attack" et pas modifiable à cause des contrôles d'intégrité. Cela devient relativement inutile d'intercepter ce genre de cookies.

Bref, si vous avez tout suivi jusqu'ici, vous devriez être interressé par une solution concrête pour sécuriser des cookies, avec plein de détails croustillants ! Je vous invite donc à lire ce papier : "A Secure Cookie Protocol" écrit par Alex X. Liu.

Dans le prochain article, nous verrons comment implémenter ce protocole en PHP. Et peut être plus tard comment l'intégrer au Zend Framework.

Les commentaires sont les bienvenus :)

Encore un blog !

Salut

Voici une version française de mon blog : http://bigornot.blogspot.com

Vous pourrez y trouver des articles sur le Developpement Web (PHP en particulier), la sécurité, linux, la "scalability", les bonnes pratiques du calibrage de bigorneaux, et tout ce qui me passe par la tête.

J'espère que ça pourra être utile à quelqu'un :)

Bonne lecture
Mat