## Gestion basique des sessions

### Sécurité des sessions

Le module de session ne peut pas garantir que les informations stockées dans une session ne sont vues uniquement par l'utilisateur qui a créé la session. Des mesures supplémentaires sont nécessaires pour protégrer la confidentialité de la session, suivant la valeur qu'on lui associe.

L'importance des données stockées dans une session doit être évaluée et des protections supplémentaires peuvent être déployées ; ceci a obligatoirement un coût tel qu'être moins pratique pour l'utilisateur. Par exemple, pour protéger les utilisateurs d'une tactique simple, la directive session.use_only_cookies doit être activée. Dans ce cas, les cookies doivent être activés obligatoirement côté client sinon les sessions ne fonctionneront pas.

Il y a plusieurs façons de divulguer des identifiants de session à des tierces personnes. I.E. injections Javascripts, identifiants de session dans les URLs, reniflement de paquets, accès physique au périphérique, etc. Un identifiant de session divulgué permet à un tiers d'accéder à toutes les ressources associées avec cet identifiant. Tout d'abord, les URLs contenant les identifiants de session. S'il y a des liens vers des sites ou des ressources externes, l'URL incluant l'identifiant de session doit être stockée dans les logs refferer du site externe. Si ces données ne sont pas chiffrées, les identifiants de session vont être transmis en texte clair sur le réseau. La solution ici est d'implémenter SSL/TLS sur le serveur et le rendre madataire pour les utilisateurs. HSTS devrait être utilisé pour améliorer également la sécurité.

> Note: Même HTTPS ne peut protégrer la confidentialité des données dans tous les cas. Par exemple, les vulnérabilités CRIME et BEAST permettent à un attaquant de lire les données. De plus, notez que certains réseaux utilisent des proxys HTTPS MITM pour des audites. Les attaquants peuvent également mettre en place ce genre de proxy.

### Gestion des sessions non adaptatives

Le gestionnaire de sessions PHP est adaptatif, par défaut. Un gestionnaire de sessions adaptatif apporte des risques supplémentaires.

Lorsque session.use_strict_mode est activé, et que le gestionnaire de sauvegarde des sessions le supporte, un identifiant de session non initialisé est rejeté, et un nouveau est créé. Ceci prévient une attaque qui force les utilisateurs à utiliser un identifiant de session connu. Un attaquant peut passer des liens ou envoyer des emails qui contient l'identifiant de session. I.e. `http://example.com/page.php?PHPSESSID=123456789` si session.use_trans_sid est activé, la victime commencera une session en utilisant l'identifiant de session fourni par l'attaquant. session.use_strict_mode permet d'annuler ce type de risque.

Avertissement

Les gestionnaires de sauvegarde définis par l'utilisateur peuvent aussi supporter le mode de session strict en implémentant la validation des identifiants de session. Tous les gestionnaires de sauvegarde définis par l'utilisateur devraient implémenter la validation des identifiants de session.

Le cookie d'identifiant de session peut être défini avec les attributs domain, path, httponly, secure et, depuis PHP 7.3, SameSite. Il existe une priorité définie par les navigateurs. En utilisant les priorités, un attaquant peut définir l'identifiant de session qui peut être utilisé en permanence. L'utilisation de la directive session.use_only_cookies ne permet pas de résoudre ce problème. session.use_strict_mode permet de mitiger ce risque. Avec la directive session.use_strict_mode=On, l'identifiant de session non initialisé sera refusé.

> Note: Même si la directive session.use_strict_mode limite les risques concernant le gestionnaire adaptatif de session, un attaquant peut forcer les utilisateurs à utiliser un identifiant de session non initialisé qui a été créé par l'attaquant, i.e. via des injections Javascript. Ce type d'attaque peut être limité en utilisant les recommandations de ce manuel. En suivant ce manuel, les développeurs devraient activer la directive session.use_strict_mode, utiliser les timestamps basés sur le gestionnaire de session, et regénérer les identifiants de session en utilisant la fonction session_regenerate_id() avec les procédures recommandées. Si les développeurs suivent ces instructions, un identifiant de session généré par un attaquant sera normalement supprimé. Lorsqu'un accès à une session obsolète survient, les développeurs devraient sauvegarder toutes les données de la session active de l'utilisateur ; ces informations seront utiles pour de futures investigations. L'utilisateur devrait être forcé à se déconnecter de toutes les sessions, i.e. le forçant ainsi à s'identifier de nouveau. Ceci permet de contrer les attaques utilisant des sessions volées.

Avertissement

L'accès à une session obsolète ne veut pas forcément dire qu'il s'agit d'une attaque. Un réseau instable et/ou l'effacement immédiat de la session active va conduire des utilisateurs légitimes à utiliser des sessions obsolètes.

Depuis 7.1.0, la fonction session_create_id() a été ajoutée. Cette fonction permet d'accéder à toutes les sessions actives d'un utilisateur en préfixant les identifiants de session avec l'identifiant de l'utilisateur. L'activation de la directive session.use_strict_mode est vital dans cette configuration. Sinon, les utilisateurs malicieux peuvent définir des identifiants de sessions pour les autres utilisateurs.

> Note: Les utilisateurs des versions antérieures à PHP 7.1.0 doivent utiliser CSPRNG, i.e. /dev/urandom, ou la fonction random_bytes() et les fonctions de hachage pour générer un nouvel identifiant de session. La fonction session_create_id() possède des mécanismes de détection de collision, et génère un identifiant de session suivant les configurations INI des sessions. L'utilisation de la fonction session_create_id() est recommandée.

### Regénération d'un identifiant de session

La directive session.use_strict_mode est un bon compromis mais n'est pas suffisant. Les développeurs doivent également utiliser la fonction session_regenerate_id() pour la sécurité des sessions.

La regénération d'un identifiant de session réduit le risque de vol d'identifiants de session, aussi, la fonction session_regenerate_id() doit être utilisée périodiquement; i.e. Regénérer l'identifiant de session toutes les 15 minutes pour sécuriser le contenu sensible. Même dans le cas où un identifiant de session est volé, à la fois le légitime utilisateur et l'attaquant aura sa session qui va expirer. En d'autres termes, l'accès au contenu par l'utilisateur ou l'attaquant va générer une erreur d'accès à une session obsolète.

Les identifiants de session doivent être regénérés lorsque les privilèges de l'utilisateur sont élevés, comme après une authentification. La fonction session_regenerate_id() doit être appelée avant le stockage des informations d'authentification dans $_SESSION. (la fonction session_regenerate_id() sauvegarde les données de session courantes automatiquement afin de sauvegarder les timestamps/etc... dans la session courante.) Assurez-vous que la nouvelle session contient le drapeau d'authentification.

Les développeurs ne doivent pas se baser sur l'expiration de l'identifiant de session définie par la directive session.gc_maxlifetime. Les attaquants peuvent accéder à l'identifiant de session de la victime de façon périodique pour éviter son expiration, et permettre son exploitation y compris avec des sessions authentifiées.

Au lieu de celà, les développeurs doivent implémenter un timestamp basé sur la gestion des données de session.

Avertissement

Même si le gestionnaire de session peut gérer les timestamps de façon transparente, cette fonctionnalité n'est pas implémentée. Les données des anciennes sessions doivent être conservés tant que le récupérateur de mémoire ne soit passé. Simultanément, les développeurs doivent s'assurer eux-même que les données de session obsolète soient effectivement effacées. Cependant, les développeurs ne doivent pas supprimer les données de session active trop rapidement. i.e. `session_regenerate_id(true);` et session_destroy() ne doivent jamais être appelés en même temps pour une session active. Ca peut paraitre contradictoire, mais c'est une exigence du mandataire.

session_regenerate_id() n'effacera pas les anciennes sessions par défaut. Les sessions authentifiées obsolètes peuvent être présente pour être utilisées. Les développeurs doivent s'assurer que les anciennes sessions ne soient pas utilisées par tout le monde. Ils doivent interdire l'accès aux données de session obsolète en utilisant eux-même des timestamps.

Avertissement

La suppression soudaine d'une session active produit des effets de bords indésirables. Les sessions peuvent disparaître lorsqu'il y a des connexions concurentes sur l'application web et/ou lorsque le réseau est instable.

Les accès potentiellement malicieux sont indétectables avec la suppression soudaine d'une session.

Au lieu de supprimer les sessions osbolètes immédiatement, les développeurs doivent définir un court temps d'expiration (timestamp) dans $_SESSION, et interdire l'accès aux données de session.

Les développeurs ne doivent pas interdire l'accès aux données des anciennes sessions immédiatement après l'exécution de la fonction session_regenerate_id(). L'accès doit être interdit à un stade ultérieure ; i.e. quelques secondes après pour les réseaux stables, comme un réseau filaire et quelques minutes après pour les réseaux instables comme des téléphones mobiles ou des réseaux Wi-Fi.

Si un utilisateur accède à une session obsolète (session ayant expirée), l'accès à cette session doit être refusé. Il est également recommandé de supprimer le statut d'authentification de toutes les sessions utilisateurs, car cela peut représenter un axe d'attaque.

L'utilisation de la directive session.use_only_cookies et de la fonction session_regenerate_id() peuvent causer des DoS personnel avec des cookies non-supprimés définis par les attaquants. Dans ce cas, les développeurs peuvent inviter les utilisateurs à supprimer les cookies et les avertir qu'ils peuvent rencontrer un problème de sécurité. Les attaquants peuvent définir des cookies malicieux via une application web vulnérable, un plugin de navigateur exposé ou vicié, un périphérique physique compromis, etc...

Avertissement

Ne vous méprenez pas sur le risque DoS. session.use_strict_mode=On est obligatoire pour la sécurité des identifiants de session ! Tous les sites sont encouragés à activer la directive session.use_strict_mode.

DoS peut uniquement survenir lorsque le compte subit une attaque. Une injection Javascript dans une application représente la plupart des axes d'attaque.

### Suppression des données de session

Les données de sessions obsolètes doivent être inaccessibles et doivent être supprimées. Le module courant de session ne prend pas en charge cet aspect.

Les données de sessions obsolètes doivent être supprimées aussi vite que possible. Cependant, les sessions actives ne doivent pas être supprimées instantanément. Pour satisfaire ces recommandations, les développeurs eux-mêmes doivent implémenter un gestionnaire des données de session basé sur un timestamp.

Définissez et gérez l'expiration du timestamp dans la variable globale $_SESSION. Interdisez l'accès aux données de sessions périmées. Lorsqu'un accès à des données de session obsolète est détecté, il convient de supprimer toutes les statuts authentifiés des sessions utilisateurs et forcer les utilisateurs à s'authentifier de nouveau. L'accès à des données de sessions obsolètes peut représenter une attaque. Pour arriver à cette fin, les développeurs doivent suivre toutes les sessions actives de tous les utilisateurs.

> Note: L'accès à une session obsolète peut également survenir à cause d'un réseau instable et/ou d'un accès concurrent à un site web, i.e. le serveur tente de définir un nouvel identifiant de session via un cookie, mais le paquet Set-Cookie n'a jamais atteint le client en raison d'une perte de connexion. Une connexion peut créer un nouvel identifiant de session via la fonction session_regenerate_id(), mais une autre connexion concurrente peut ne pas avoir encore reçu l'identifiant de session. Toutefois, les développeurs doivent interdire l'accès à une session obsolète à un moment plus éloigné. i.e. la gestion des sessions basés sur le timestamp est obligatoire.

En résumé, les données de sessions ne doivent pas être détruites avec la fonction session_regenerate_id(), ni avec la fonction session_destroy(), mais les timestamps doivent être utilisés pour contrôler l'accès aux données de session. Laissez la fonction session_gc() supprimer les données obsolètes depuis le stockage des données de sessions.

### Session et Verrouillage

Les données de session sont verrouillées par défaut pour éviter les accès concurent. Le verrouillage est obligatoire pour conserver une consistance des données de session au travers les requêtes.

Cependant, le verrouillage de session peut être utilisé par les attaquants pour réaliser des attaques DoS. Pour minimiser le risque d'une attaque DoS par verrouillage de session, il convient de minimiser les verrous. Utilisez des données en lecture seule lorsque les données de session n'ont pas besoin d'être mises à jour. Utilisez l'option 'read_and_close' avec la fonction session_start(). `session_start(['read_and_close'=>1]);` va clôre la session aussi vite que possible après la mise à jour de la variable globale $_SESSION en utilisant la fonction session_commit().

Le module de session courant ne détecte pas toutes les modifications de la variable $_SESSION lorsque la session est inactive. Il en va de la responsabilité du développeur de ne pas modifier la variable $_SESSION lorsque la session est inactive.

### Sessions actives

Les développeurs doivent conserver une traces de toutes les sessions actives de chaque utilisateur, et leur notifier le nombre de sessions actives, depuis quelle adresse IP, depuis combien de temps, etc. PHP ne conserve pas de traces de ces informations. Les développeurs sont supposés le faire eux même.

Il existe différentes façons de faire celà. Une implémentation possible est de définir une base de données qui conserve une trace des données nécessaires, et y stoquer toutes les informations pertinentes. Depuis que les données de session sont GCed, les développeurs doivent faire attention aux données GCed pour maintenir la base de données des sessions actives consistante.

Une des implémentations simple est "l'identifiant utilisateur préfixant l'identifiant de session" et stoquer les informations nécessaires dans la variable $_SESSION. La plupart des bases de données sont relativement performantes pour sélectionner un préfixe sous la forme d'une chaîne de caractères. Les développeurs DOIVENT utiliser la fonction session_regenerate_id() ainsi que la fonction session_create_id() pour celà.

Avertissement

N'utilisez jamais de données confidentielles comme préfixe. Si l'identifiant utilisateur est confidentiel, vous devriez utiliser la fonction hash_hmac().

Avertissement

L'activation de la directive session.use_strict_mode est obligatoire pour ce type de configuration. Assurez vous qu'il est activé. Sinon, la base de données des sessions actives peut être compromise.

Le gestionnaire de session basé sur un timestamp est obligatoire pour détecter l'accès à des sessions obsolètes. Lorsque l'accès à une session obsolète est détecté, le drapeau d'authentification doit être supprimé de toutes les sessions actives de l'utilisateur. Ceci permet d'éviter aux attaquants de continuer à exploiter les sessions volées.

### Session et l'auto-identification

Les développeurs ne doivent pas utiliser d'identifiants de session avec une grande durée de vie pour l'auto-identification, car cela accroit le risque d'utiliser des sessions volées. Une fonctionnalité d'auto-identification doit être implémenté par le développeur.

Utilisez une clé de hachage sécurisé à usage unique comme clé d'auto-identification en utilisant la fonction setcookie(). Utilisez un hachage sécurisé plus fort que SHA-2. i.e. SHA-256 ou supérieur avec des données aléatoires depuis la fonction random_bytes() ou via /dev/urandom.

Si l'utilisateur est non authentifié, vérifiez si la clé d'auto-identification à usage unique est valide ou non. Dans ce cas où elle est valide, authentifiez l'utilisateur et définissez une nouvelle clé de hachage sécurisée à usage unique. Une clé d'auto-identification ne doit être utilisée qu'une seule fois, i.e. n'utilsez jamais une clé d'auto-identification, et regénérez la toujours.

Une clé d'auto-identification est une clé d'authentification avec une longue durée, elle doit être protégée autant que possible. Utilisez les attributs de cookie path/httponly/secure/SameSite pour la sécuriter. i.e. ne transmettez jamais la clé d'auto-identification tant que celà n'est pas nécessaire.

Les développeurs doivent implémenter les fonctionnalités qui désactivent l'auto-identification, et suppriment les cookies contenant les clés d'auto-identification non nécessaires.

### Attaques CSRF (Cross-Site Request Forgeries)

Les sessions et les authentifications ne protègent pas contre les attaques CSRF. Les développeurs doivent implémenter des protections CRSF eux mêmes.

La fonction output_add_rewrite_var() peut être utilisée pour la protection CSRF. Référez vous aux pages du manuel pour plus de détails.

> Note: PHP, avant sa version 7.2.0, utilise le même buffer de sortie et les mêmes configurations INI que la configuration trans-sid. Toutefois, l'utilisation de la fonction output_add_rewrite_var() avec les versions de PHP antérieures à 7.2.0 n'est pas conseillé.

La plupart des frameworks d'applications web supporte la protection CSRF. Référez vous au manuel de votre framework d'application web pour plus de détails.

Depuis PHP 7.3, l'attribut SameSite du cookie de session peut être défini. Ceci est une mesure supplémentaire qui peut minimiser les vulnérabilités CSRF.