Générer une URL unique * et * aléatoire en C #

Mon objective ultime est de créer une URL unique et impossible à deviner / à prédire. Cette URL a pour but de permettre aux utilisateurs d’effectuer des opérations telles que la vérification de leur adresse électronique et la réinitialisation de leur mot de passe. Ces URL expireraient dans un délai déterminé (actuellement fixé à 24 heures).

J’utilisais à l’origine un Guid dans ce but, mais je comprends maintenant qu’il se situe entre “très bien” et “très peu sûr”, selon l’expert que vous écoutez. J’ai donc pensé renforcer mon code un peu, au cas où. Au début, je pensais utiliser un Guid , mais le générer à partir d’octets aléatoires plutôt que de la Guid.NewGuid() . Voici la méthode que je suis venu avec:

 public static Guid GetRandomGuid() { var bytes = new byte[16]; var generator = new RNGCryptoServiceProvider(); generator.GetBytes(bytes); return new Guid(bytes); } 

Je ne vois pas très bien ce qui se passe exactement lorsque vous utilisez un new Guid(bytes) au lieu de Guid.NewGuid() , mais je pense que toute cette méthode génère un tableau d’octets aléatoires et le stocke dans une structure de données Guid . En d’autres termes, il n’est plus garanti d’être unique, mais aléatoire.

Comme mes URL doivent être à la fois uniques et aléatoires, cela ne semble pas être une solution solide. Je pense maintenant que je devrais baser mes URL sur une combinaison d’un identifiant unique (qui pourrait être un Guid ou, le cas échéant, d’un identifiant auto-incrémenté de firebase database) et d’une séquence aléatoire générée à partir de RNGCryptoServiceProvider .

Des questions

Quel est le meilleur moyen de générer une URL de vérification / réinitialisation du mot de passe qui soit à la fois unique et extrêmement difficile / impossible à prévoir / à deviner?

  • Devrais-je simplement construire mon URL en concaténant une chaîne unique avec une chaîne aléatoire?

  • Le .NET Framework a-t-il un moyen intégré de générer facilement une séquence unique et aléatoire (imprévisible) pouvant être utilisée pour les URL?

  • Si non, existe-t-il une bonne solution open source disponible?

Mettre à jour

Au cas où quelqu’un aurait une exigence similaire, j’utilise actuellement la méthode suivante:

 public static ssortingng GenerateUniqueRandomToken(int uniqueId) // generates a unique, random, and alphanumeric token { const ssortingng availableChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; using (var generator = new RNGCryptoServiceProvider()) { var bytes = new byte[16]; generator.GetBytes(bytes); var chars = bytes .Select(b => availableChars[b % availableChars.Length]); var token = new ssortingng(chars.ToArray()); return uniqueId + token; } } 

Veuillez commenter si vous voyez des problèmes avec cela.

Un Guid n’est pas censé être un élément de sécurité. Les normes relatives à la création d’un GUID n’affirment en aucun cas son degré de sécurité , ni sa difficulté à deviner. Il fait simplement une déclaration sur son caractère unique.

Si vous tentez de sécuriser votre système à l’aide d’un GUID, vous ne disposerez pas d’un système sécurisé.

[Edit: j’ai manqué l’appel à RNGCryptoServiceProvider ci-dessus. Excuses à ce sujet.]

Le problème avec les générateurs RNG tels que le RNGCryptoServiceProvider pour votre cas est qu’ils ne garantissent pas l’unicité. Comme vous le savez, les statistiques ne sont pas statistiquement uniques mais ne la garantissent pas. Le seul moyen de garantir à la fois l’unicité et le caractère aléatoire est de générer le GUID, comme vous l’avez ensuite placé dans un magasin interrogeable, comme une table de firebase database. Chaque fois que vous générez une nouvelle valeur, vérifiez qu’elle n’est pas déjà présente. Si c’est le cas, jetez-le et générez une nouvelle valeur.

Vous pouvez générer des nombres uniques «aléatoires» sur 128 bits en exécutant un compteur via un compteur AES associé à une clé aléatoire. Tant que la même clé est utilisée, cela ne répétera jamais aucune sortie.

 static byte[] AESCounter(byte[] key, ulong counter) { byte[] InputBlock = new byte[16]; InputBlock[0] = (byte)(counter & 0xffL); InputBlock[1] = (byte)((counter & 0xff00L) >> 8); InputBlock[2] = (byte)((counter & 0xff0000L) >> 16); InputBlock[3] = (byte)((counter & 0xff000000L) >> 24); InputBlock[4] = (byte)((counter & 0xff00000000L) >> 32); InputBlock[5] = (byte)((counter & 0xff0000000000L) >> 40); InputBlock[6] = (byte)((counter & 0xff000000000000L) >> 48); InputBlock[7] = (byte)((counter & 0xff00000000000000L) >> 54); using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider()) { AES.Key = key; AES.Mode = CipherMode.ECB; AES.Padding = PaddingMode.None; using (ICryptoTransform Encryptor = AES.CreateEncryptor()) { return Encryptor.TransformFinalBlock(InputBlock, 0, 16); } } } 

Obtenez md5 de toutes les informations de connexion de l’utilisateur et concaténez-le avec le GUID généré par Guid.NewGuid (). ToSsortingng () et utilisez-le dans votre URL, cela devrait fonctionner correctement.

Créez une table dans la firebase database avec un linkID et une colonne Datesent. Lors de la génération du lien, insérez DateTime.Now dans la table et renvoyez linkId, définissez le linkID en tant que paramètre de chaîne de requête sur activationLink.

Lors du chargement de la page d’activation, récupérez l’identifiant linkId et utilisez-le pour évoquer une procédure stockée qui renverra la date de passage de l’ID linkId correspondant en tant que paramètre. Lorsque vous récupérerez la date, vous pourrez indiquer combien de temps vous souhaitez que le lien rest actif. using the .AddDays() / .AddMonths (il s’agit des méthodes C # pour datetime). Comparez ensuite la date de votre retour avec la date du jour. S’il a passé plusieurs jours ou mois, envoyez un message d’erreur ou continuez et affichez le contenu de la page.

Je vous suggère de conserver le contenu de la page dans un panneau et de le rendre visible = "false" puis de ne rendre le panneau visible="true" si la date est toujours dans la plage.