web-dev-qa-db-fra.com

Comment implémenter en toute sécurité une fonctionnalité "Se souvenir de moi"?

En supposant que vous ayez déjà un site Web qui implémente tous les éléments de connexion standard, quelle est la manière correcte et la plus sécurisée pour permettre aux utilisateurs de se connecter automatiquement pendant une certaine période (disons 30 jours)? Ce délai doit être strictement respecté; c'est-à-dire que je ne pense pas qu'une solution valide serait de donner une date d'expiration à un cookie et d'espérer que le navigateur de l'utilisateur le supprime en temps opportun.

Détails du système de connexion existant:

  • Un utilisateur doit entrer un nom d'utilisateur et un mot de passe
  • La base de données stocke les noms d'utilisateur ainsi que strong_hash_function (mot de passe + user_specific_random_salt)
  • Les formulaires de connexion sont chargés et envoyés via SSL, comme toutes les pages suivantes

Il existe de nombreux exemples et beaucoup présentent des failles de sécurité évidentes. Utilisons PHP comme langue cible mais les concepts devraient être applicables à n'importe quelle langue.

57
colithium

Ma réponse est incomplète et a un défaut non trivial, dans certaines situations - veuillez voir @ la réponse de Scott à la place.


Ma réponse comporte deux parties:

Tout d'abord, en supposant que votre modèle de menace ne se soucie pas de l'exposition des cookies côté client , vous pouvez générer et stocker un nonce côté serveur, hacher cela avec le nom d'utilisateur et autres informations (par exemple, adresse IP du client, nom d'ordinateur, horodatage, éléments similaires), et envoyez-les dans le cookie. Le nonce doit être stocké dans la base de données, avec la date d'expiration, et tous deux vérifiés lorsque le cookie revient.
Il ne doit s'agir que d'un cookie de "souvenir", PAS d'un cookie de session - c'est-à-dire lorsque vous obtenez ce cookie sans session, réémettez un nouveau cookie de session régulière. Notez également que, comme le cookie de session, il ne doit être livré que via https et en utilisant les attributs secure et httpOnly, et bien sûr, le cookie doit être correctement défini, etc.

Deuxièmement, pour les utilisateurs dont on se souvient, vous devez (probablement, selon la sensibilité) invoquer un processus de réauthentification pour certaines opérations sensibles. Autrement dit, parfois, ils devraient de nouveau saisir leur mot de passe, mais rarement, et uniquement pour des opérations sensibles. Il s'agit d'empêcher les attaques telles que CSRF et l'exposition de bureau partagée.

31
AviD

J'ai longuement écrit sur connexions sécurisées et cases à cocher "Se souvenir de moi" . La réponse acceptée n'est pas fausse, mais je dirais que c'est un peu plus compliqué à un égard qu'il ne devrait l'être, et néglige un domaine où un peu plus de complexité est nécessaire.

générer et stocker un nonce côté serveur, hacher celui-ci avec le nom d'utilisateur et d'autres informations (par exemple, l'adresse IP du client, le nom de l'ordinateur, l'horodatage, des choses similaires), et l'envoyer dans le cookie. Le nonce doit être stocké dans la base de données, avec la date d'expiration, et tous deux vérifiés lorsque le cookie revient.

Ainsi, une implémentation qui n'expose aucune information au client pourrait ressembler à ...

// Storage:
setcookie(
    'rememberme',
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce),
    time() + 8640000 // 100 days, for example
);
// Validation:
$valid = hash_equals(
    $_COOKIE['rememberme'],
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce)
);

La limitation évidente ici est que, si votre adresse IP (ou d'autres informations) change, votre cookie est inutile. Certains pourraient voir cela comme une bonne chose, je le vois comme inutilement hostile à la convivialité pour les utilisateurs de Tor.

Vous pouvez certainement interpréter la citation ci-dessus comme signifiant quelque chose de différent, comme un jeton Web JSON d'un pauvre homme aussi. Cependant, les cookies HTTP sont limités à 4 Ko par domaine. L'espace est limité.

Un autre problème: combien d'informations votre requête de base de données fuit-elle sur votre application? (Oui, je parle de canaux secondaires.)


Stratégie sécurisée "Remember Me" de Paragon Initiative

Espace de rangement:

  1. Générez une chaîne aléatoire de 9 octets à partir de random_bytes() (PHP 7.0+ ou via random_compat ), base64 l'encode en 12. Ceci sera utilisé pour les recherches de base de données.
  2. Générez une autre chaîne aléatoire, de préférence d'au moins 18 octets, et encore une fois en base64, codez-la (jusqu'à 24+). Ce sera réellement utilisé pour l'authentification.
  3. Stockez $lookup Et hash('sha256, $validator) dans la base de données; bien sûr associé à un compte utilisateur spécifique.
  4. Stockez $lookup . $validator Dans le cookie HTTP de l'utilisateur (par exemple rememberme).

Validation (connexion automatique):

  1. Divisez le cookie en $lookup Et $validator.
  2. Effectuer une recherche de base de données basée sur $lookup; ce n'est pas grave s'il y a un canal latéral de synchronisation ici.
  3. Vérifiez hash_equals($row['hashedValdator'], hash('sha256', $validator)).
  4. Si l'étape 3 renvoie TRUE, associez la session en cours au compte d'utilisateur approprié.

Analyse de sécurité:

  • Quels canaux secondaires sont atténués?
    Plus important encore: il atténue l'impact des fuites d'informations de synchronisation sur l'opération de comparaison de chaînes utilisée dans la recherche de base de données.

    Si vous avez implémenté une authentification par jeton aléatoire naïf, un attaquant pourrait envoyer un grand nombre de demandes jusqu'à ce qu'il trouve un jeton d'authentification valide pour un utilisateur. (Ils ne seraient probablement pas en mesure de sélectionner la victime dont ils usurpent l'identité.)

  • Et si un attaquant pouvait divulguer la table de base de données d'authentification à long terme?
    Nous avons stocké un hachage SHA256 du $validator Dans la base de données, mais le texte en clair du hachage est stocké dans le cookie. Étant donné que l'entrée est une valeur d'entropie élevée, il est peu probable que la recherche par force brute donne des résultats. (Cela réduit également le besoin, par exemple, de bcrypt.)

Cette stratégie est implémentée dans Gatekeeper .

10
Scott Arciszewski

Voilà une question intéressante. Je commencerai par dire que je ne suis pas sûr que cela puisse se faire sans affaiblir la sécurité de l'application, mais en fonction du site qui peut être considéré comme méritant le compromis.

Donc, une approche pourrait être

La base de données d'état de session devra enregistrer l'heure à laquelle un jeton est défini et la durée à laquelle il doit être mémorisé afin de pouvoir comparer le jeton présenté.

Lorsque l'utilisateur se connecte à l'application, il a une case à cocher qui lui permet de rester connecté (idéalement avec une plage de périodes que l'utilisateur peut sélectionner)

Lorsque le jeton de session est défini, cette période est utilisée comme expiration du cookie. Lors des visites ultérieures, le cookie sera présenté à l'application et le serveur devra vérifier s'il est toujours valide (c'est-à-dire que la période d'expiration de la fonction mémoriser n'a pas été atteinte). Si le jeton est toujours valide, l'utilisateur est traité comme authentifié, sinon le jeton est effacé et l'utilisateur est redirigé vers l'écran de connexion.

Problèmes avec cette approche. Les navigateurs partagés signifieraient que l'accès non autorisé est possible.

Toute personne pouvant accéder au cookie (soit par une vulnérabilité du site, un problème de navigateur ou par le biais de logiciels malveillants installés sur la machine) pourra également obtenir un accès non autorisé au site. Une suggestion courante pour atténuer cela est d'essayer de lier le cookie à une adresse IP source (cryptée ou stockée sur le serveur bien sûr) qui est vérifiée lorsque l'utilisateur présente le cookie, mais cela pose des problèmes avec les grands environnements d'entreprise où un utilisateur peut venir à partir d'un certain nombre d'adresses IP, et peuvent également être susceptibles d'être usurpés.

5
Rory McCune

Vous n'avez pas à dépendre du navigateur pour supprimer le cookie, vous avez le site vérifier la date d'expiration sur le cookie.

En fait, pour que cela soit sécurisé, je dirais que c'est probablement ce que vous devrez faire de toute façon, car vous voudriez définir la date d'expiration comme partie de la valeur du cookie et la crypter plutôt que de compter sur le propriété d'expiration en texte brut du cookie lui-même.

Et vous voudriez vraiment marquer le cookie comme sécurisé et utiliser un cookie secondaire pour la durée d'une session individuelle, ou protéger la session entière (pas seulement le processus de connexion) avec SSL pour empêcher le cookie d'être volé sur le fil et utilisé par une partie non autorisée.

4
Xander

Cet article décrit une approche qui se défend contre le vol de cookies et la devinette des identifiants de session:

  • Un cookie "se souvenir de moi" se compose de l'ID utilisateur, d'un jeton (grand nombre aléatoire) et d'un jeton de séquence (un autre grand nombre aléatoire).
  • Lorsqu'un utilisateur présente le cookie, la base de données est recherchée pour ces trois informations.
    • S'il est trouvé, le jeton de séquence est régénéré, stocké dans la base de données et envoyé à l'utilisateur
    • Si seuls le nom d'utilisateur et le jeton sont trouvés, il est présumé que quelqu'un d'autre a volé le cookie car le jeton de séquence a déjà été utilisé. Le système peut alors avertir l'utilisateur et/ou supprimer tous les jetons stockés de cet utilisateur de la base de données.

Comme mesure de sécurité supplémentaire, il est conseillé que des actions critiques telles que la modification des données de connexion ou le paiement de choses demandent le mot de passe, si l'utilisateur n'a été authentifié qu'avec le cookie "se souvenir de moi".

La table de base de données qui contient l'ID utilisateur et les jetons contient également la date d'expiration des jetons. Il peut également contenir l'agent utilisateur et l'adresse IP afin qu'un attaquant doive également les répliquer. Tous les jetons ne doivent être stockés que comme des hachages car ils fonctionnent essentiellement comme des mots de passe et permettraient à un attaquant, qui a obtenu la base de données, de se connecter.

J'ai créé un exemple d'implémentation pour PHP.

0
chiborg