Quelles meilleures pratiques devraient être utilisées dans un script de connexion PHP?


27

Je souhaite réécrire mes scripts de connexion pour les sites Web des clients afin de les rendre plus sécurisés. Je veux savoir quelles meilleures pratiques je peux mettre en œuvre dans ce domaine. Les panneaux de contrôle protégés par mot de passe sont nombreux, mais très peu semblent implémenter les meilleures pratiques en termes d'écriture de code, de vitesse et de sécurité.

J'utiliserai PHP et une base de données MYSQL.

J'avais l'habitude d'utiliser md5, mais je vois que sha256 ou sha512 serait mieux (avec du hachage sécurisé et du sel).

Certains scripts de connexion enregistrent l'adresse IP tout au long de la session ou même l'agent utilisateur, mais je veux éviter cela car il n'est pas compatible avec les serveurs proxy.

Je suis également un peu en retard sur les meilleures pratiques d'utilisation des sessions en PHP 5 (la dernière fois que j'ai lu était PHP 4), donc certaines des meilleures pratiques avec cela seraient utiles.

Merci.


1
Jetez votre script sur codereview.SE!
Chris

Aide @Chris CodeReview pour la clarté du code, etc. Vous n'êtes certainement pas censé aller vers eux pour des raisons de sécurité. Si quoi que ce soit, ils vous rappelleront presque toujours de ne pas "lancer la vôtre", ce qui est fondamentalement la même réponse qu'il va obtenir dans n'importe quelle communauté de programmation sur SE.
Alternatex

Réponses:


21

Le mieux est de ne pas réinventer la roue. Mais, je comprends, dans le monde PHP, il peut être difficile de trouver un composant de haute qualité qui le fait déjà (même je suis presque sûr que les frameworks implémentent de telles choses et que leurs implémentations sont déjà testées, solides, révisées par le code, etc. )

Si, pour certaines raisons, vous ne pouvez pas utiliser un framework, voici quelques suggestions:

Suggestions liées à la sécurité

  • Utilisez PBKDF2 ou Bcrypt si vous le pouvez . C'est fait pour ça.

    Justification: les deux algorithmes peuvent ralentir arbitrairement le processus de hachage, ce qui est exactement ce que vous voulez lorsque vous hachez des mots de passe (des alternatives plus rapides signifient une force brute plus facile). Idéalement, vous devez ajuster les paramètres afin que le processus devienne de plus en plus lent au fil du temps sur le même matériel, tandis qu'un nouveau matériel plus rapide est publié.

  • Si vous ne pouvez pas, au moins n'utilisez pas MD5 / SHA1. Jamais. Oublie ça . Utilisez SHA512 à la place, par exemple. Utilisez aussi du sel.

    Justification: MD5 et SHA1 sont trop rapides. Si l'attaquant a accès à votre base de données contenant les hachages et possède une machine (pas même particulièrement) puissante, le forçage brutal d'un mot de passe est rapide et facile. S'il n'y a pas de sels, les chances que l'attaquant trouve le mot de passe réel augmentent (ce qui pourrait nuire davantage si le mot de passe était réutilisé ailleurs).

  • En PHP 5.5.0 et versions ultérieures, utilisez password_hashetpassword_verify .

    Justification: appeler une fonction fournie par le framework est facile, donc le risque de faire une erreur est réduit. Avec ces deux fonctions, vous n'avez pas à penser à différents paramètres tels que le hachage. La première fonction renvoie une seule chaîne qui peut ensuite être stockée dans la base de données. La deuxième fonction utilise cette chaîne pour la vérification du mot de passe.

  • Protégez-vous de la force brute . Si l'utilisateur soumet un mauvais mot de passe alors qu'il a déjà soumis un autre mauvais mot de passe il y a 0,01 seconde, c'est une bonne raison de le bloquer. Alors que les êtres humains peuvent saisir rapidement, ils peuvent probablement pas que rapides.

    Une autre protection serait de fixer une limite de pannes par heure. Si l'utilisateur a soumis 3600 mots de passe erronés en une heure, 1 mot de passe par seconde, il est difficile de croire qu'il s'agit d'un utilisateur légitime.

    Raisonnement: si vos mots de passe sont hachés de manière non sécurisée, la force brute peut être très efficace. Si les mots de passe sont stockés en toute sécurité, la force brute gaspille toujours les ressources et la bande passante du réseau de votre serveur, ce qui réduit les performances des utilisateurs légitimes. La détection de la force brute n'est pas facile à développer et à obtenir, mais pour tout système, mais minuscule, cela en vaut la peine.

  • Ne demandez pas à vos utilisateurs de changer leur mot de passe toutes les quatre semaines. Ceci est extrêmement ennuyeux et diminue la sécurité, car il encourage la sécurité post-it.

    Justification: l'idée que forcer les mots de passe à changer toutes les n semaines protège le système de la force brute est fausse. Les attaques par force brute réussissent généralement en quelques secondes, minutes, heures ou jours, ce qui rend les changements de mot de passe mensuels non pertinents. D'un autre côté, les utilisateurs se souviennent mal des mots de passe. Si, par ailleurs, ils doivent les changer, ils tenteront soit d'utiliser des mots de passe très simples, soit de simplement noter leurs mots de passe sur les post-its.

  • Auditez tout, à chaque fois. Stockez les ouvertures de session, mais ne stockez jamais les mots de passe dans le journal d'audit. Assurez-vous que le journal d'audit ne peut pas être modifié (c'est-à-dire que vous pouvez ajouter des données à la fin, mais pas modifier les données existantes). Assurez-vous que les journaux d'audit sont soumis à une sauvegarde régulière. Idéalement, les journaux devraient être stockés sur un serveur dédié avec des accès très restrictifs: si un autre serveur est piraté, l'attaquant ne pourra pas effacer les journaux pour cacher sa présence (et le chemin emprunté pendant l'attaque).

  • Ne me souviens pas des informations d'identification de l'utilisateur dans les cookies, sauf si l'utilisateur demande à le faire (la case "Se souvenir de moi" doit être décochée par défaut pour éviter les erreurs humaines).

Suggestions de facilité d'utilisation

  • Laissez l'utilisateur se souvenir du mot de passe s'il le souhaite, même si la plupart des navigateurs sont déjà dotés de cette fonctionnalité.
  • N'utilisez pas l'approche Google lorsque, au lieu de demander un nom d'utilisateur et un mot de passe, l'utilisateur est parfois invité à saisir un mot de passe uniquement , le nom d'utilisateur étant déjà affiché dans<span/> . Les navigateurs ne peuvent pas remplir le champ de mot de passe dans ce cas (au moins Firefox ne peut pas le faire), il oblige donc à se déconnecter, puis à se connecter avec un formulaire ordinaire, rempli par le navigateur.
  • N'utilisez pas de fenêtres contextuelles activées par JavaScript pour la connexion. Il brise les fonctionnalités de rappel de mot de passe des navigateurs (et craint dans tous les cas).
  • Laissez l'utilisateur entrer son nom d'utilisateur ou son adresse e-mail . Lorsque je m'inscris, parfois le nom d'utilisateur que je veux saisir est déjà pris, je dois donc en inventer un nouveau. J'ai toutes les chances d'oublier ce nom en deux heures.
  • Gardez toujours un lien vers "Mot de passe oublié" près du formulaire de connexion. Ne l'affichez que lorsque l'utilisateur n'a pas réussi à se connecter: l'utilisateur qui ne se souvient pas du tout de son mot de passe n'a aucune idée qu'elle doit soumettre un mauvais afin de voir le lien "Mot de passe oublié".
  • N'utilisez pas la sécurité en post-it .
  • N'inventez pas de règles stupides liées au mot de passe afin de le rendre plus faible . Exemple: "Votre mot de passe doit commencer par une lettre minuscule."; "Votre mot de passe ne peut pas contenir d'espaces."

Pas rencontré bcrypt. Quel est votre raisonnement pour recommander cela? Pourquoi pas md5 / sha1? Merci pour le reste des suggestions!
baritoneuk

Le raisonnement est simple: il bcryptest fait par des personnes hautement qualifiées. Il est également testé, révisé, etc. Il n'y a donc aucune raison d'implémenter la même chose vous-même. C'est comme créer votre propre algorithme de cryptographie pour votre site Web. * "Pourquoi pas md5 / sha1?": Voir par exemple en.wikipedia.org/wiki/MD5#Security
Arseni Mourzenko

5
bcrypt est lent et comme mentionné, mis en œuvre par des professionnels. md5 est un algorithme de hachage et non un cryptage qui ne sont pas tout à fait la même chose. Le hachage rapide d'un fichier est bien, md5 serait utilisé ici ... le hachage d'un mot de passe n'a pas besoin d'être aussi rapide, bcrypt peut vous aider ici. La raison pour laquelle je dis cela est que la force brute devient plus difficile lorsque l'algorithme de cryptage est "lent".
Chris

2
@baritoneuk: le minimum est cool. Mais je ne peux pas compter le nombre de sites auxquels je suis allé qui avaient des restrictions telles que des longueurs maximales , pas de caractères non alphanumériques, etc. , ils le stockent simplement dans leur base de données en clair.
Carson63000

1
@Brian Ortiz: pas toujours. C'est parfois une bonne idée de fixer une limite. Si vous ne le faites pas, testez-vous si votre application fonctionne pour n'importe quelle longueur? Comment? Si vous ne le testez pas, comment pouvez-vous être sûr que cela fonctionne? Au lieu de cela, lorsque vous définissez une limite à une valeur raisonnable, comme 100, vous pouvez facilement tester un mot de passe de 100 caractères.
Arseni Mourzenko

6
  1. Votre site doit utiliser HTTPS. À aucun moment, vous ne devez présenter une page de connexion ou accepter les connexions à partir d'une connexion non cryptée.
  2. Vos cookies doivent être limités à HTTP uniquement et limités à des connexions sécurisées si vous utilisez HTTPS.
  3. Le processus de connexion ne devrait pas prendre moins de 2 secondes (1 si vous pensez que 2 secondes sont trop longues). Utilisez un hachage sécurisé lors du stockage et de la vérification des mots de passe, et utilisez un sel plus difficile à deviner. Utilisez bcryptsi possible. Sinon, utilisez un autre type de hachage itéré.
  4. Ne composez jamais vos requêtes de base de données d'une manière qui nécessite l'utilisation de fonctions telles que mysql_real_escape_string. N'utilisez jamais la concaténation de chaînes pour créer votre requête. Utilisez des requêtes préparées et paramétrées. La plupart, sinon la totalité, des pilotes DB pour PHP le prennent en charge. Si vous ne savez pas comment le faire, passer un peu de temps à apprendre comment prepare, bindet en executeutilisant tous les DB que vous utilisez. C'est le seul moyen sûr de se prémunir contre l'injection SQL. PDO prend cela en charge.
  5. Encouragez vos utilisateurs à ne pas utiliser le même mot de passe qu'ils utilisent pour leur e-mail. Rappelez-leur que si votre site est compromis et s'ils utilisent le même mot de passe aux deux endroits, quelqu'un peut détourner leur e-mail.

+1 pour HTTPS, car il y a de plus en plus de trafic via les réseaux WiFi publics. Mais je ne suis pas d'accord avec le point 3: 2 secondes est trop long du point de vue des utilisateurs. 0,5 s. est très bien, surtout si d'autres techniques anti-force brute sont utilisées.
Arseni Mourzenko

Je suis confus sur le point numéro 4. Comment échappez-vous à la convention d'échapper aux données avec mysql_real_escape_string? Toutes les données soumises par les utilisateurs ne doivent pas être approuvées et filtrées en conséquence.
chrisw

@chrisw: Oui, toutes les entrées utilisateur doivent être traitées comme si elles étaient malveillantes. Mais lorsque vous utilisez des requêtes paramétrées, c'est, sans exception, le meilleur moyen d'arrêter l'injection SQL . Si vous n'utilisez pas de requêtes préparées et paramétrées, vous êtes vulnérable à l'injection SQL. mysql_real_escape_stringest une fausse sécurité - ce n'est pas une garantie. Les requêtes paramétrées le sont.
greyfade

Je suis d'accord maintenant après avoir fait quelques lectures supplémentaires. La fonction: mysql_real_escape_string est généralement sûre pour la plupart, je ne considérerais pas cela comme une énorme responsabilité, mais je peux voir comment l'utilisation des instructions préparées est beaucoup plus efficace.
chrisw

1
Vous pouvez utiliser PDO.
Felix G

1

Utilisez un hachage unidirectionnel salé (de préférence SHA512 en utilisant http://au2.php.net/manual/en/function.hash.php ) ... de cette façon, si quelqu'un pirate votre base de données, même s'il connaît le sel , ils ne peuvent pas extraire les mots de passe (sans une table arc-en-ciel, ce qui serait trop long pour SHA512).

Utilisez HTTPS.

N'envoyez pas de confirmation de nom d'utilisateur / mot de passe par e-mail à l'utilisateur lors de son inscription.

Si vous autorisez les cookies pour «se souvenir de moi» - ajoutez une connexion supplémentaire pour accéder aux fonctions d'administration et de modification de profil.

Si un utilisateur obtient un mot de passe erroné, ne dites pas qu'il s'est trompé de mot de passe (disons qu'il a toujours eu le 'nom d'utilisateur ou mot de passe incorrect') - de cette façon, moins d'indices sur les noms d'utilisateur valides.

Essayez d'implémenter le nom d'écran par rapport à la connexion - de cette façon, moins d'utilisateurs afficheront leur nom de connexion sur les listes d'utilisateurs.


Entièrement d'accord avec le peu de ne pas envoyer d'e-mails en texte brut par e-mails. J'ai perdu le compte des fois où les sites Web font cela. Si vous avez 1 mot de passe pour tous les sites (ce qui, heureusement, je n'en ai pas), tous les sites Web sont potentiellement compromis. Ces sites Web stockent probablement également les mots de passe en texte brut. soupir
baritoneuk

1
  • N'utilisez jamais d'algorithmes de hachage MD5 ou SHA1. Tenez-vous toujours aux plus récents comme SHA512
  • Utilisez toujours un sel fort généré de façon aléatoire
  • N'envoyez jamais à vos utilisateurs leurs mots de passe, même en cas de "Mot de passe oublié"
  • N'utilisez jamais les fonctions mysql_ *. Ils sont amortis depuis longtemps. Stick to PDO
  • Ne stockez jamais de mots de passe dans des sessions ou des cookies

0

Voici un conseil: assurez-vous que vos cookies ne sont pas accessibles avec JS pour empêcher le vol, voir l'article Protéger vos cookies: article HttpOnly de Jeff Atwood

... Grâce à une construction intelligente, l'URL mal formée parvient juste à grincer devant le désinfectant. Le code rendu final, lorsqu'il est affiché dans le navigateur, charge et exécute un script à partir de ce serveur distant.

... celui qui charge cette page de profil utilisateur injecté par script vient de transmettre involontairement les cookies de son navigateur à un serveur distant maléfique!

Comme nous l'avons déjà établi, une fois que quelqu'un a les cookies de votre navigateur pour un site Web donné, il y a essentiellement les clés du royaume pour votre identité ...

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.