web-dev-qa-db-fra.com

Chaîne d'identification séquentielle qui ne peut pas être rétroconçue (le problème du "numéro de facture")

Disons que j'exploite un site Web où vous pouvez créer des photos de chats. Je donne à chaque photo de chat un identifiant unique afin qu'elle puisse être partagée sur les réseaux sociaux avec http://catpictures.com/base62Identifier.

Je pourrais donner aux images de chat des identifiants séquentiels tels que 1,2,3, etc., mais il serait alors possible de découvrir facilement combien de nouvelles images de chat les utilisateurs créent par jour (par le plus grand identifiant qui renvoie HTTP 200 chaque jour). Cela m'expose à la stratégie courante consistant à commander un produit à vos concurrents une fois par mois et à noter le numéro de facture. Les chiffres du trafic sur le site Web sont bien corrélés aux revenus de l'entreprise, donc je veux évidemment garder ces informations secrètes.

Ce que j'envisage d'essayer:

Cela ressemble à un travail pour un algorithme de hachage, non? Le problème est qu'en observant un hachage, il est assez facile de dire quel algorithme l'a créé (md5, crc32, etc.). Quelqu'un avec une table Rainbow ferait court de cette idée. Je pourrais saler l'identifiant [hachage ("sel" +1), hachage ("sel" +2), ...], mais j'aurais alors à me soucier de la sécurité associée au sel. Et la vérification des collisions.

Une autre idée que j'ai eue était de générer une chaîne aléatoire de caractères et de l'utiliser comme clé primaire de l'image de chat dans la base de données (ou simplement je pouvais hacher les n premiers bits des données d'image de chat). De cette façon, je n'aurais qu'à vérifier les collisions.

Existe-t-il une méthode standard et conforme aux meilleures pratiques pour éviter d'exposer vos volumes de trafic via vos URL à identifiant unique?

Edit: Je recherche spécifiquement une solution qui soit une bonne combinaison de sécurité et d'adéquation en tant que clé primaire de base de données ou colonne indexable.

52
Escher

L'approche standard de ce type de problème consiste à créer un ID (Universally Unique Identifier) pour chaque image. Il s'agit généralement d'un identifiant aléatoire de 128 bits que vous pouvez attribuer à chaque image sans craindre en particulier qu'il soit possible d'énumérer les images via une attaque par force brute sur l'espace de noms.

Par exemple, dans .NET, vous pouvez utiliser la structure GUID pour ce type de but. Depuis Windows 20 ( source ), Guid.NewGuid génère un UUID aléatoire (version 4). (Les versions anciennes généraient un ID version 1 qui révèle la date à laquelle il a été généré, ne faisant rien pour vous protéger du problème du "numéro de facture".)

78
Rory McCune

J'utiliserais simplement le hachage d'image. Quel est le problème avec quelqu'un qui détermine le hachage que vous avez utilisé? Si je pense que "cette partie de l'URL ressemble à un sha1", téléchargez le fichier et il a que sha1, j'avais raison. Mais cela ne me rend pas capable de briser votre "sécurité de chat". Même s'il était possible de tenter de casser le hachage pour comprendre l'image, il n'y a aucun intérêt à tenter cela au lieu de simplement le télécharger.

30
Ángel

Générez simplement un hachage cryptographique sécurisé des données d'image et utilisez-le comme identifiant.

Cela a deux effets secondaires:

  • Les gens peuvent savoir si une image existe déjà sur votre service en demandant une image avec ce hachage.
  • Les gens ne peuvent pas télécharger d'images en double.

Ces deux effets ne sont pas intrinsèquement mauvais. Ils pourraient même être utiles. Mais si vous souhaitez les éviter, vous pouvez saler chaque hachage d'image avec un numéro pseudo-aléatoire à partir d'un générateur de nombres aléatoires sécurisé.

Soit dit en passant, les collisions n'ont rien à craindre. Avec une fonction de hachage comme SHA256, les chances d'une collision aléatoire sont si astronomiquement petites, ce serait une sensation quand vous en trouveriez une .

14
Philipp

La méthode standard consiste simplement à générer aléatoirement vos URL à l'aide d'un générateur de nombres pseudo-aléatoires (CSPRNG) cryptographiquement sécurisé.

Pas besoin de hachage ou similaire - utilisez simplement de vieux nombres aléatoires. Ils ne doivent pas non plus être des GUID (sauf si votre base de données gère mieux les GUID que les simples chiffres pour une raison quelconque). Vraisemblablement, votre site se souvient déjà de l'image qui est accessible à chaque URL, il vous suffit donc de la modifier pour gérer les URL aléatoires au lieu des URL séquentielles.

Un nombre aléatoire de 128 bits doit être suffisamment long.

N'oubliez pas de vérifier les URL en double lors du traitement de nouvelles images.

9
user253751

D'après ce que j'ai lu dans la question, les commentaires et autres réponses, tout tourne autour de la recherche d'un identifiant unique pour chaque image, ce qui n'est pas devinable, ni ne fournirait d'informations sur le nombre d'images, et serait facile à manipuler dans une base de données.

Alors, pourquoi ne pas simplement utiliser l'horodatage d'insertion (nombre de millisecondes depuis 1970)? S'il y a une probabilité pour que deux personnes insèrent une image de chat dans la même milliseconde, vous pouvez la concaténer avec un nombre séquentiel correspondant au nombre d'insertion dans cette milliseconde.

De cette façon, la seule chose que quelqu'un recherchant agressivement votre dernière photo découvrirait est la dernière fois que quelqu'un a ajouté une photo à condition de laisser un tel imbécile faire ce qui ressemblerait à une attaque quotidienne.

En attendant, vous n'auriez aucun problème avec les collisions ou le support de la base de données.

8
Aldian

Solution alternative:

Ajoutez des métadonnées à vos identificateurs d'image. Exemple:

philipp_20151213_00002.jpg - Deuxième image postée par l'utilisateur Philipp le 13 décembre 2015.

J'ai des fuites sur ces métadonnées, mais ce ne sont que des données qu'un utilisateur peut voir quand il clique sur ce lien (je suppose).

Cela ne dit pas à un observateur combien d'images sont affichées au total sur votre service, à peu près l'activité de cet utilisateur particulier ce jour-là. Si vous souhaitez masquer cela également, vous pouvez utiliser des nombres pseudo-aléatoires au lieu de nombres séquentiels. Les collisions peuvent toujours être possibles lorsqu'un seul utilisateur télécharge une très grande quantité d'images en une journée, mais elles seront suffisamment rares pour que vous puissiez les gérer en générant simplement de nouveaux nombres aléatoires jusqu'à ce que vous en ayez un qui n'est pas pris.

6
Philipp

Voici une méthode. Conservez un CSPRNG à l'échelle du serveur de 8 octets. Ensuite, pour chaque nouvelle image, générez un autre CSPRNG de 8 octets. Hachez ce CSPRNG avec votre CSPRNG à l'échelle du serveur (md5 est très bien). Puis XOR les 8 derniers octets du hachage avec l'ID d'image (qui incrémentera automatiquement à partir de 0 dans une base de données). Le client recevra un encodage Base64 du CSPRNG unique de 8 octets de l'image le long de avec le résultat de 8 octets XOR. Ce sera l'ID d'image publique.

Lorsque le serveur reçoit l'ID d'image publique, il hache les 8 premiers octets de l'ID public avec le CSPRNG à l'échelle du serveur de 8 octets. Ensuite, il faudra les 8 derniers octets du hachage et XOR avec les 8 derniers octets de l'ID public. Le résultat serait l'ID interne privé qui peut être indexé à partir de la base de données.

Mise à jour (explication):

Tout d'abord, prédéfinissez un CSPRNG global aléatoire qui sera utilisé pour tous les calculs d'ID (8 octets ou 64 bits avec 18 446 744 073 709 551 616 combinaisons possibles).

serverCSPRNG = CSPRNG(8)

Pour créer un nouvel ID public (16 octets) à partir d'un privateID (8 octets), procédez comme suit:

newCSPRNG = CSPRNG(8)
hashEnding = last8Bytes(md5(newCSPRNG + serverCSPRNG))
publicID = newCSPRNG + XOR(hashEnding, privateID)

Pour dériver le privateID du publicID:

hashEnding = last8Bytes(md5(first8Bytes(publicID) + serverCSPRNG))
privateID = XOR(hashEnding, last8Bytes(publicID))

Pour plus de sécurité, un CSPRNG global secondaire (serveur statique uniquement) peut être XOR sur les 8 derniers octets de l'ID public afin de le protéger complètement des attaques par force brute (car il implémente le modèle de sécurité inhérent à un horodateur).

1
Jonathan Gray

Comme indiqué ici : les hachages, les UUID, etc. ont le `` désavantage '' que les insertions d'enregistrements dans la base de données où ces hachages/uuides sont le PK et le PK est groupé sont peut-être très chers (définissez cher .. .) car ils ne sont généralement pas séquentiels (à moins qu'une fonction spécifique comme NEWSEQUENTIALID soit utilisée, cependant: notez le bloc "important" sur cette page: " Si la confidentialité est un problème, n'utilisez pas cette fonction. Il est possible de deviner la valeur du prochain GUID généré ... ").

En dehors des suggestions ici, je considérerais quelque chose comme Twitter ( interromp ) flocon de neige . J'ai écrit une bibliothèque .Net similaire ( IdGen ); c'est readme a quelques informations sur la façon dont cela fonctionne exactement. L'avantage est que les identifiants générés sont toujours (principalement) séquentiels, pas trop gourmands en espace (UHID 64 bits contre 128 bits) et peuvent être utilisés dans un environnement distribué (non coordonné) où vous avez plusieurs hôtes/processus générant des identifiants sans provoquer de collisions. Et bien qu'ils soient séquentiels, ils ne donnent pas beaucoup d'informations sur le nombre de photos de chats (ou, plus généralement, le nombre de "ID utilisés") sur une certaine période de temps.

1
RobIII

Cela ressemble à un travail pour un algorithme de hachage, non?

Non, car en observant, vous devez vous soucier des collisions. Pour moi, cela ressemble à un travail pour une permutation, c'est-à-dire un chiffrement par bloc. Cela nécessite la gestion d'une clé, ce qui est l'inconvénient, mais cela vous permet d'utiliser la fonction d'incrémentation automatique de votre base de données et de ne pas vous soucier des collisions.

La partie délicate consiste à décider quoi faire à propos de l'IV, et ici vous avez des options. Vous pouvez en générer une nouvelle à chaque fois que vous créez une URL, il y aura donc potentiellement, par exemple, 2 ^ 128 URL différentes par photo de chat. Vous pouvez faire en sorte que l'IV soit par utilisateur ou par session et stocké côté serveur dans le cadre du profil utilisateur/état de session. Vous pouvez même le faire être par utilisateur mais inclus dans l'URL, afin que vous puissiez suivre qui réussit à rendre les images virales.

1
Peter Taylor

Problème:

  • Nous souhaitons avoir un nombre séquentiel; sinon, cela coûte cher d'ajouter des enregistrements à la base de données car le milieu des index doit être mis à jour dans un ordre principalement aléatoire.
  • Nous ne voulons pas que le nombre soit lié au nombre de chats téléchargés.
  • Nous avons besoin que le numéro soit unique, mais uniquement sur votre site Web.

Donc:

  • nextCat est défini sur 0 lorsque le site Web premier démarre, il devra probablement être de 64 bits.
  • nextCat est incremented chaque fois qu'un chat est ajouté, et newCat est défini sur true.
  • nextCat est incremented par une minuterie aléatoire qui se déclenche à une vitesse plus rapide que celle que vous attendez des chats. Cependant, si newCat est true, l'incrémentation n'est pas effectuée pour ce déclenchement de minuterie et newCat est défini sur false.
  • chaque chat reçoit également un GUID, mais n'a jamais besoin d'être trouvé en fonction de son GUID
  • l'adresse Web d'un chat est quelque chose.com/cats/catNumber-catGuid
  • si lorsqu'un chat est demandé, le catGuid est erroné, la même réponse est donnée alors pour un catNumber qui ne se rapporte pas à un chat.

(La minuterie est effectuée pour une durée aléatoire, de sorte qu'il est difficile de dire si deux chats sont ajoutés entre un déclenchement de la minuterie.)

0
Ian Ringrose

Une approche consiste à utiliser hashids .

Hashids est une petite bibliothèque open source qui génère des identifiants courts, uniques et non séquentiels à partir de nombres.

Il convertit des nombres comme 347 en chaînes comme "yr8", ou un tableau de nombres comme [27, 986] en "3kTMd".

Vous pouvez également décoder ces identifiants. Ceci est utile pour regrouper plusieurs paramètres en un seul ou simplement les utiliser comme UID courts.

Vos performances de base de données ne sont pas altérées car vous pouvez continuer à utiliser des ID séquentiels numériques en interne. Pendant ce temps, les touches externes sont opaques.

0
Alfred Armstrong

J'ai une solution low-tech à ce problème. Utilisez simplement un service de raccourcissement d'URL ou écrivez le vôtre.

Il fournit les éléments suivants:

  1. Votre URL publique n'est pas exposée sur les sites de médias sociaux.
  2. Vos URL sont garanties d'être aléatoires et arbitraires.
  3. Vous êtes libre de modifier votre implémentation sous-jacente de la dénomination des ressources et les liens externes continueront de fonctionner.
  4. Partage plus facile http://catpic.to/i34dhY contre. http://catpictures.com/some-guid-string.
  5. L'identifiant unique est facilement indexé/recherché.

Si vous ne voulez pas compter sur un service tiers, vous pouvez facilement lancer le vôtre en implémentant une fonction bijective dans la langue de votre choix.

0
Burhan Khalid