IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Tutoriel : comment gérer correctement les sessions applicatives en PHP et le problème des cookies,
Un billet blog de tse_jc

Le , par tse_jc

0PARTAGES

1. Le problème des cookies dans la gestion des sessions applicatives PHP

Pour aller droit au but, placer les cookies au centre de votre stratégie de gestion des sessions PHP pour vos applications web est une très mauvaise idée et reste à proscrire. En voici les principales raisons.
- Un cookie est stocké au niveau client applicatif et donc à l'extérieur de l'environnement de gestion d'une application web qui se situe côté serveur.
- Étant dans un environnement n tiers, chaque tiers ne peut faire confiance à ce qu'il reçoit de l'autre. C'est la règle fondamentale à appliquer au niveau sécuritaire.
En conséquence, ce qui est placé du côté navigateur client relève du domaine "public" pour votre application, ce qui ne peut être décemment considéré comme source de confiance pour authentifier un utilisateur au sein de votre applicatif.

Rappel : Un serveur PHP utilise déjà un cookie en gestion interne pour authentifier un utilisateur sur le serveur web. C'est une obligation technique, il reste incontournable et reste transparent pour tout le monde. Il ne sert à rien à rajouter un cookie applicatif qui reste une aberration à la vue de ce qui précède.

Utilisez des cookies sur vos sites, à des fins marketing et de prospection, en respectant la réglementation, est une façon justifiée d'utiliser des cookies, et je vous recommande vivement de vous en tenir à cette utilisation professionnellement parlant.

2. Les besoins techniques
a. Une session PHP ne doit pas être trop longue
Plusieurs raisons à cela. Comme il est expliqué dans la documentation PHP, le module de sessions PHP n'est pas protégé naturellement contre les attaques au niveau des sessions, notamment via javascript. Plus une session est longue, plus elle a de chances d'être interceptée et utilisée pour accéder à des informations normalement protégées.
Principe n°1
La première chose donc pour limiter la surface d'attaque de nos applications en PHP, consistera à faire en sorte d'avoir des sessions relativement restreintes dans la durée.
Seulement dans une application web d'entreprise, les utilisateurs détestent devoir se reconnecter 50 fois dans la même journée, surtout si le mot de passe est compliqué. Idéalement, il faut éviter de le faire plus de 2 fois par jour. Le plus généralement constaté, après discussion avec des responsables en entreprise sur les enjeux au niveau sécurité, il est généralement souhaité une période de session sans interruption comprise entre 3 et 4h.

Une des premières solutions se présentant à nous pour résoudre ce problème et atteindre ce minima de 3 à 4 heures est de jouer sur la configuration de PHP au niveau du serveur afin de repousser le timeout de connexion PHP et la période du Garbage collector à cette limite. Le seul avantage est la simplicité de gestion ici, mais voilà : les désavantages sont très nombreux. Parmi les principaux, citons :


    [*=2]La réduction de la capacité de monter en charge du serveur HTTP, et la réduction drastique du nombre de connexions concurrentes pouvant être gérées par le serveur. En effet l’optimisation d’un serveur web passe avant tout par la réduction optimale du temps de réponse d’un serveur à une requête HTTP/HTTPS afin d’en libérer les ressources au plus tôt et permettre à une autre requête de se présenter.
    [*=2]On ne respecte pas le principe n°1 pour réduire la surface d’attaque de notre application.

Principe n°2
Les vieilles sessions doivent être maintenues jusqu'à leur prise en charge par le gc (garbage collector)
Ne pas faire ainsi s'est s'exposer à des comportements instables voire erratiques dans un contexte concurrentiel.
Principe n°3
session_regenerate_id(true) et session_destroy() ne doivent jamais être utilisées ensemble pour une session active.
Ce principe est directement issu du principe précédent.
b. Gérer les sessions via un double axe au minimum : PHP + Base de données et idéalement sur un axe triple : PHP + DB + Cache Redis
Une fois éliminé la solution précédente qui fait le travail certes, mais qui n'est pas satisfaisante techniquement (dans un contexte d'optimisation d'infrastructure) et surtout au niveau sécurité, il s'impose de suite la gestion selon un axe double.

Axe double : PHP + DB
Si on souhaite garder des sessions PHP à durée standard dans une configuration standard par défaut pour le moins d’un côté et satisfaire à notre clientèle, en leur permettant de ne pas se reconnecter avant 3 à 4 heures de travail, tout en ne travaillant pas sur leur poste de travail en mode continu, mais permettre un travail effectué par intermittence et donc avec des périodes d’inactivité incluses dans ce laps de temps imparti, on n’a pas le choix :

=> On gère la durée de connexion applicative en base de données et on gère les sessions techniques pour traiter les requêtes clients sur le serveur applicatif du côté PHP.

On peut donc déduire d'un tel scénario les évènements suivants :


    [*=1]Une session applicative aura plusieurs id de session PHP différentes au cours de sa vie.
    [*=1]Le gc va intervenir plusieurs fois au cours de la vie de notre session applicative.
    [*=1]A chaque intervention du gc on va devoir maintenir le contenu de la session PHP
    [*=1]le gc va intervenir sur des sessions passées et obsolètes ( pas de problème de transmission de session), et peut intervenir aussi sur la session en cours en cas de période d'inactivité trop longue de l'utilisateur (problème de transmission de session).


Gestion du problème de transmission du contenu de session
En cas de période d'inactivité trop longue de l'utilisateur, il y a un risque de perte de contenu de session, et ce, d'autant plus que la période du gc fixé est faible. Il faut donc stocker le contenu sérialisé des informations de session ailleurs que dans le système de gestion de sessions de PHP et ailleurs que dans un cookie bien évidemment. Une des meilleures pratique qui est recommandée dans la documentation de PHP est de l'écrire dans un fichier sur disque hors dossier de sessions donc.

=> L'endroit ou l'on va sauvegarder ces informations de session constitue donc le 3e axe de gestion.

Si l'on reste ici sur un paradigme PHP+DB, on devra stocker l'information en DB.

Rappel de ce qui est stocké au niveau session



    [*=1]Il n'y a pas de variable globale stockée directement en session pour préserver les principes de la POO.
    [*=1]On recommande donc d'y stocker des objets sérialisés. En général, comme on l'a vu dans un précédent billet, il y a en a au moins 3 : une classe utilisateur, une classe connexion et une classe module applicatif.


Les données de type utilisateur et connexion sont limitées et peuvent être restituées par la base de données. Il s'agit tout au plus d'une dizaine de variables selon l'applicatif concerné.
Les données de type module applicatif, quant à elles, sont spécifiques à la navigation de l'utilisateur et de ce qu'il a fait au cours de sa session de travail. Il y aura donc dans ce type de données des données calculées spécifiques mises en cache. Elles sont de nature non prédictive et il peut être très coûteux de les recharger à partir de la base de données. Pour le moins, l'expérience utilisateur s'en ferait sentir dans un contexte de charge au niveau des performances applicatives, si l'on décidait de ne pas maintenir ces données dans ce contexte de transmission de session.

On pourrait bien évidemment scinder la gestion des deux premiers types de données avec le troisième. Dans un tel scénario qui obligerait à stocker en variable globale cette fois un tableau sérialisé des modules consultés par l'utilisateur et à gérer spécifiquement leur contenu à part hors db pour éviter de la solliciter et garder une capacité de montée en charge applicative optimisée, il y aurait un surcoût de gestion applicatif au prix de quelques efforts, mais cela resterait à prototyper au niveau performances pour se faire une bonne idée de la chose. Toujours dans un tel scénario, le rechargement des deux premiers types de données à partir de la base, bien que très limité et localisé, provoquerait une augmentation de charge préjudiciable à l'applicatif.

Préférer/Privilégier une gestion à trois axes
Du plus performant au moins performant, voici les choix à privilégier dans un tel contexte de gestion de session.


    [*=1]Sauvegarde de l'intégralité du cache de session (3 types de données) dans REDIS

Le cache étant très rapide et géré en mémoire, il n'y aura pas plus performant. Attention cependant, gérer les sessions PHP directement dans REDIS n'a rien à voir avec ce qui est exposé ici, puisque le cache de session PHP dans REDIS serait soumis aux mêmes règles du gc que s'il était géré de façon traditionnelle. Cette solution à l'avantage en plus, d'être transparent techniquement dans le cloud.


    [*=1]Sauvegarde de l'intégralité du cache de session (3 types de données) sur disque

Solution à privilégier par défaut donc surtout lorsqu'on est pas encore à l'aise avec la gestion d'un cache spécifique comme REDIS.

Note
Que cela soit avec REDIS ou directement sur Disque, rien n'empêche d'utiliser un plugin PHP de binarisation de données sérialisées (plugin compilé sur serveur) qui apporte un gain de performances notable dans un contexte de forte charge applicative et qui a fait ses preuves, même si cela ajoute un traitement supplémentaire des données concernées. Un tel système est implémentable sur Microsoft Azure.

2. Algorithme d'implémentation
Je recommande de diviser votre algorithme en deux parties distinctes. La première concerne la connexion initiale de l'utilisateur, lorsqu'il se connecte à l'application. La seconde concerne le ping de connexion au moment où une requête utilisateur est envoyée au serveur (rafraichissement du navigateur, requête ajax, ...). On préfèrera travailler ainsi en mode passif (pull) sur action de l'utilisateur pour optimiser la capacité de montée en charge du serveur (on libère les ressources au plus tôt) plutôt qu'en push, qui consomme des ressources en permanence et limite potentiellement le nombre d'accès concurrentiels à l'applicatif beaucoup plus vite.

Il est probable voire fortement probable (je ne l'espère pas) que vous soyez obligé de mettre en place des variables de gestion qui n'existaient pas jusqu'à maintenant dans votre environnement de gestion de sessions applicatif.
Voici celles que j'implémente dans mes solutions et qui me paraissent incontournables et minimales.

  • datetime de connexion
  • datetime dernière requête serveur
  • session_id() PHP
  • Informations navigateur utilisateur ayant initié la connexion
  • Adresse IP utilisateur connecté ayant initié la connexion
  • Id technique de l'utilisateur connecté
  • Les données sérialisées à maintenir
  • La durée maximale de session applicative
  • La durée de vie configurée de votre gc


Remarques / Conseils
Essayez de ne conserver et de gérer au niveau des variables de gestion que le strict nécessaire pour offrir un service de qualité et sécurisé, tout en ayant la possibilité d'auditer sereinement vos sessions en cas d'anomalie constatée. Trop de volumétrie au niveau de la base a aussi ses conséquences et ne reste pas anodine.

À vous d'implémenter vos algorithmes

Une erreur dans cette actualité ? Signalez-nous-la !