Fixation / piratage de session PHP

145

J'essaie de mieux comprendre la fixation et le détournement de session PHP et comment éviter ces problèmes. J'ai lu les deux articles suivants sur le site Web de Chris Shiflett:

Cependant, je ne suis pas sûr de bien comprendre les choses.

Pour éviter la fixation de session, il suffit d'appeler session_regenerate_id (true); après avoir réussi à connecter quelqu'un? Je pense avoir bien compris.

Il parle également d'utiliser des jetons transmis dans les URL via $ _GET pour empêcher le piratage de session. Comment cela se ferait-il exactement? Je suppose que lorsque quelqu'un se connecte, vous générez son jeton et le stockez dans une variable de session, puis sur chaque page, vous compareriez cette variable de session avec la valeur de la variable $ _GET?

Ce jeton ne devrait-il être changé qu'une seule fois par session ou à chaque chargement de page?

Est-ce également un bon moyen d'empêcher le piratage sans avoir à transmettre une valeur dans les URL? ce serait beaucoup plus facile.

moi2
la source
Vous pourriez peut-être ajouter des liens vers les pages où vous avez trouvé ces recommandations.
Gumbo

Réponses:

220

Ok, il y a deux problèmes distincts mais liés, et chacun est traité différemment.

Fixation de session

C'est là qu'un attaquant définit explicitement l'identifiant de session d'une session pour un utilisateur. En général, en PHP, cela se fait en leur donnant une URL comme http://www.example.com/index...?session_name=sessionid. Une fois que l'attaquant donne l'URL au client, l'attaque est identique à une attaque de détournement de session.

Il existe plusieurs façons d'éviter la fixation de session (faites-les toutes):

  • Définissez session.use_trans_sid = 0dans votre php.inifichier. Cela indiquera à PHP de ne pas inclure l'identifiant dans l'URL et de ne pas lire l'URL pour les identifiants.

  • Définissez session.use_only_cookies = 1dans votre php.inifichier. Cela indiquera à PHP de ne jamais utiliser d'URL avec des identifiants de session.

  • Régénérez l'ID de session chaque fois que l'état de la session change. Cela signifie l'un des éléments suivants:

    • Authentification d'utilisateur
    • Stockage d'informations sensibles dans la session
    • Changer quoi que ce soit dans la session
    • etc...

Détournement de session

C'est là qu'un attaquant récupère un identifiant de session et est capable d'envoyer des requêtes comme s'il s'agissait de cet utilisateur. Cela signifie que puisque l'attaquant possède l'identifiant, il est pratiquement impossible de les distinguer de l'utilisateur valide en ce qui concerne le serveur.

Vous ne pouvez pas empêcher directement le détournement de session. Vous pouvez cependant prendre des mesures pour le rendre très difficile et plus difficile à utiliser.

  • Utilisez un identifiant de hachage de session fort: session.hash_functiondans php.ini. Si PHP <5.3, définissez-le sur session.hash_function = 1pour SHA1. Si PHP> = 5.3, définissez-le sur session.hash_function = sha256ou session.hash_function = sha512.

  • Envoyez un hash fort: session.hash_bits_per_characterdans php.ini. Réglez ceci sur session.hash_bits_per_character = 5. Bien que cela ne rende pas plus difficile la fissuration, cela fait une différence lorsque l'attaquant tente de deviner l'identifiant de session. L'ID sera plus court, mais utilisera plus de caractères.

  • Définissez une entropie supplémentaire avec session.entropy_fileet session.entropy_lengthdans votre php.inifichier. Définissez le premier sur session.entropy_file = /dev/urandomet le second sur le nombre d'octets qui seront lus à partir du fichier d'entropie, par exemple session.entropy_length = 256.

  • Modifiez le nom de la session du PHPSESSID par défaut. Ceci est accompli en appelant session_name()avec votre propre nom d'identifiant comme premier paramètre avant l'appel session_start.

  • Si vous êtes vraiment paranoïaque, vous pouvez également faire pivoter le nom de la session, mais sachez que toutes les sessions seront automatiquement invalidées si vous le modifiez (par exemple, si vous le rendez dépendant de l'heure). Mais en fonction de votre cas d'utilisation, cela peut être une option ...

  • Faites souvent pivoter votre identifiant de session. Je ne ferais pas cela à chaque demande (sauf si vous avez vraiment besoin de ce niveau de sécurité), mais à un intervalle aléatoire. Vous voulez changer cela souvent car si un attaquant détourne une session, vous ne voulez pas qu'il puisse l'utiliser trop longtemps.

  • Incluez l' agent utilisateur de$_SERVER['HTTP_USER_AGENT'] dans la session. En gros, lorsque la session démarre, stockez-la dans quelque chose comme $_SESSION['user_agent']. Ensuite, à chaque demande suivante, vérifiez qu'elle correspond. Notez que cela peut être truqué, donc ce n'est pas fiable à 100%, mais c'est mieux que pas.

  • Incluez l' adresse IP de$_SERVER['REMOTE_ADDR'] l' utilisateur dans la session. En gros, lorsque la session démarre, stockez-la dans quelque chose comme $_SESSION['remote_ip']. Cela peut être problématique de la part de certains FAI qui utilisent plusieurs adresses IP pour leurs utilisateurs (comme AOL le faisait auparavant). Mais si vous l'utilisez, ce sera beaucoup plus sûr. Le seul moyen pour un attaquant de simuler l'adresse IP est de compromettre le réseau à un moment donné entre l'utilisateur réel et vous. Et s'ils compromettent le réseau, ils peuvent faire bien pire qu'un détournement (comme les attaques MITM, etc.).

  • Incluez un jeton dans la session et du côté des navigateurs que vous incrémentez et comparez souvent. Fondamentalement, pour chaque demande, faites $_SESSION['counter']++du côté serveur. Faites également quelque chose dans JS du côté des navigateurs pour faire de même (en utilisant un stockage local). Ensuite, lorsque vous envoyez une requête, prenez simplement un nonce d'un jeton et vérifiez que le nonce est le même sur le serveur. En faisant cela, vous devriez être en mesure de détecter une session détournée car l'attaquant n'aura pas le compteur exact, ou s'il le fait, vous aurez 2 systèmes transmettant le même nombre et pourrez dire qu'un est falsifié. Cela ne fonctionnera pas pour toutes les applications, mais c'est un moyen de lutter contre le problème.

Une note sur les deux

La différence entre la fixation de session et le détournement concerne uniquement la manière dont l'identifiant de session est compromis. Lors de la fixation, l'identifiant est mis à une valeur que l'attaquant connaît à l'avance. Dans Hijacking, il est soit deviné, soit volé à l'utilisateur. Sinon, les effets des deux sont les mêmes une fois que l'identifiant est compromis.

Régénération d'identifiant de session

Chaque fois que vous régénérez l'identifiant de session en utilisant session_regenerate_idl'ancienne session doit être supprimé. Cela se produit de manière transparente avec le gestionnaire de session principal. Cependant, certains gestionnaires de session personnalisés qui utilisentsession_set_save_handler() ne le font pas et sont susceptibles d'attaquer d'anciens identificateurs de session. Assurez-vous que si vous utilisez un gestionnaire de session personnalisé, que vous gardez une trace de l'identifiant que vous ouvrez, et si ce n'est pas le même que vous enregistrez que vous supprimez (ou modifiez) explicitement l'identifiant de l'ancien.

En utilisant le gestionnaire de session par défaut, vous pouvez simplement appeler session_regenerate_id(true). Cela supprimera les anciennes informations de session pour vous. L'ancien ID n'est plus valide et entraînera la création d'une nouvelle session si l'attaquant (ou n'importe qui d'autre d'ailleurs) essaie de l'utiliser. Soyez prudent avec les gestionnaires de session personnalisés ...

Détruire une session

Si vous allez détruire une session (lors de la déconnexion par exemple), assurez-vous de la détruire complètement. Cela inclut la désactivation du cookie. Utilisation session_destroy:

function destroySession() {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
    session_destroy();
}
ircmaxell
la source
4
L'utilisation de 5 au lieu de 4 bits par caractère ne change en rien la «force» (quelle que soit la «force» dans ce cas). Mais bien que vos points soient généralement souhaitables, ils manquent de détails importants. Par exemple, qu'arrive-t-il à la session associée à l'ancien ID de session ou comment une session avec un ancien ID de session doit être gérée une fois qu'elle est devenue invalide.
Gumbo
2
@battal: Non, c'est le but. session_regenerate_idn'invalide pas la session qui est toujours associée à l'ancien ID; seulement si le paramètre delete_old_session est défini sur true, la session sera détruite. Mais que se passe-t-il si un attaquant a lancé cette régénération d'identifiant?
Gumbo
6
Je ne suis pas d'accord avec la régénération de session chaque fois que vous modifiez une variable de session, cela ne devrait être fait que lors de la connexion / déconnexion. Vérifier également l'agent utilisateur n'a pas de sens et vérifier REMOTE_ADDR est problématique. Une chose que je voudrais ajouter est session.entropy_file = /dev/urandom. La génération d'entropie interne de PHP s'est avérée extrêmement faible et le pool d'entropie fourni par / dev / random ou / dev / uranom est le meilleur que vous puissiez obtenir sur un serveur Web sans rng matériel.
tour du
4
Vous devez également ajouter session.cookie_httponlyet session.cookie_secure. Le premier aide à contrecarrer xss (mais ce n'est pas parfait). Le 2e est le meilleur moyen d'arrêter OWASP A9 ...
rook
4
Je ne comprends pas une si bonne réponse mais il manque l'élément le plus important: utilisez SSL / HTTPS. L'incrémentation du compteur est une source de problème avec plusieurs requêtes rapides les unes après les autres, un utilisateur actualise une page deux fois ou frappe deux fois un bouton de soumission. La solution d'adresse IP est un problème de nos jours avec tous les utilisateurs mobiles et les IP en constante évolution. Vous pouvez regarder le premier ensemble de l'adresse IP, mais cela pose toujours des problèmes. Le mieux est d'empêcher la découverte de l'identifiant de session en premier lieu et cela utilise SSL / HTTPS.
Sanne
37

Les deux attaques de session ont le même objectif: accéder à une session légitime d'un autre utilisateur. Mais les vecteurs d'attaque sont différents:

Dans les deux attaques, l'ID de session correspond aux données sensibles sur lesquelles ces attaques se concentrent. C'est donc l'ID de session qui doit être protégé à la fois pour un accès en lecture (Session Hijacking) et un accès en écriture (Session Fixation).

La règle générale de protection des données sensibles à l'aide de HTTPS s'applique également dans ce cas. De plus, vous devez effectuer les opérations suivantes:

Pour éviter les attaques de fixation de session , assurez-vous que:

Pour empêcher les attaques de piratage de session , assurez-vous que:

Pour éviter les deux attaques de session, assurez-vous que:

  • pour n'accepter que les sessions lancées par votre application. Vous pouvez le faire en prenant une empreinte digitale d'une session lors de l'initiation avec des informations spécifiques au client. Vous pouvez utiliser l' ID utilisateur-agent, mais n'utilisez pas l'adresse IP distante ou toute autre information susceptible de changer entre les demandes.
  • pour changer l'ID de session en utilisant session_regenerate_id(true)après une tentative d'authentification ( trueuniquement en cas de succès) ou un changement de privilèges et détruire l'ancienne session. (Assurez-vous de stocker toutes les modifications d' $_SESSIONutilisation session_write_close avant de régénérer l'ID si vous souhaitez conserver la session associée à l'ancien ID; sinon, seule la session avec le nouvel ID sera affectée par ces modifications.)
  • pour utiliser une implémentation d'expiration de session appropriée (voir Comment faire expirer une session PHP après 30 minutes? ).
Gombo
la source
Article génial, en particulier la dernière section.
Mattis
6

Les jetons que vous mentionnez sont un nombre "nonce" utilisé une fois. Ils ne doivent pas nécessairement être utilisés une seule fois, mais plus ils sont utilisés longtemps, plus les chances que le nonce puisse être capturé et utilisé pour détourner la session sont élevées.

Un autre inconvénient des nonces est qu'il est très difficile de créer un système qui les utilise et autorise plusieurs fenêtres parallèles sur le même formulaire. Par exemple, l'utilisateur ouvre deux fenêtres sur un forum et commence à travailler sur deux messages:

window 'A' loads first and gets nonce 'P'
window 'B' loads second and gets nonce 'Q'

Si vous n'avez aucun moyen de suivre plusieurs fenêtres, vous n'aurez stocké qu'un seul nonce - celui de la fenêtre B / Q. Lorsque l'utilisateur soumet ensuite son message à partir de la fenêtre A et passe en nonce «P», le système rejettera le message comme P != Q.

Marc B
la source
Alors qu'est-ce que cela a à voir avec la fixation de session?
tour du
2
Il a un point valable en particulier dans le domaine de l'utilisation simultanée de nombreuses requêtes AJAX.
DanielG
2

Je n'ai pas lu l'article de Shiflett, mais je pense que vous avez mal compris quelque chose.

Par défaut, PHP transmet le jeton de session dans l'URL chaque fois que le client n'accepte pas les cookies. Sinon, dans le cas le plus courant, le jeton de session est stocké sous forme de cookie.

Cela signifie que si vous mettez un jeton de session dans l'URL, PHP le reconnaîtra et essaiera de l'utiliser par la suite. La fixation de session se produit lorsque quelqu'un crée une session, puis incite un autre utilisateur à partager la même session en ouvrant une URL qui contient le jeton de session. Si l'utilisateur s'authentifie d'une manière ou d'une autre, l'utilisateur malveillant connaît alors le jeton de session d'un utilisateur authentifié, qui peut avoir des privilèges différents.

Comme je suis sûr que Shiflett l'explique, la chose habituelle à faire est de régénérer un jeton différent chaque fois que les privilèges d'un utilisateur changent.

Andrea
la source
Pour ajouter à cela, assurez-vous de détruire toutes les sessions précédemment ouvertes car elles seront toujours valides avec les autorisations utilisateur existantes.
corrodedmonkee
0

Oui, vous pouvez empêcher la fixation de session en régénérant l'identifiant de session une fois lors de la connexion. De cette façon, si l'attaquant ne connaîtra pas la valeur du cookie de la session nouvellement authentifiée. Une autre approche qui arrête totalement le problème est définie session.use_only_cookies=Truedans votre configuration d'exécution. Un attaquant ne peut pas définir la valeur d'un cookie dans le contexte d'un autre domaine. La fixation de session repose sur l'envoi de la valeur du cookie en tant que GET ou POST.

tour
la source