web-dev-qa-db-fra.com

Quelle stratégie pour crypter les données auxquelles accèdent plusieurs utilisateurs?

J'ai besoin de suggestions pour concevoir mon architecture de base de données (dans le cadre d'une application web) sur le point particulier de son cryptage; sachant que les éléments suivants doivent être respectés:

1 - Les données doivent être cryptées en toute sécurité dans la base de données

Il s'agit de se protéger contre les attaquants, et principalement pour que les utilisateurs sachent que même le personnel ne peut pas accéder à leurs données, les clés ne doivent donc pas être accessibles par l'équipe technique.

2 - Les données sont étendues aux comptes d'utilisateurs

(ce qui signifie: chaque utilisateur possède son propre ensemble de données, lié à son ID utilisateur)

J'ai donc pensé utiliser le mot de passe de l'utilisateur comme clé de cryptage, mais cela pose un problème: lorsque le propriétaire des données décide de changer de mot de passe, les données doivent être rechiffrées et ce serait trop de demande en alimentation du serveur.

- Le propriétaire des données chiffrées doit pouvoir donner accès à ses données à d'autres utilisateurs

(ce qui signifie: il existe un système d'invitation et une partie ou la totalité des données d'un utilisateur sont accessibles par d'autres utilisateurs invités)

Ce qui rend impossible l'utilisation du mot de passe de l'utilisateur pour crypter les données car nous ne voulons pas partager notre mot de passe.

J'ai donc pensé à un cryptage à clé privée/publique, mais la clé privée doit être stockée quelque part. Le stocker dans la base de données rend tout le cryptage inutile; et le stockage côté client n'est pas possible non plus car cela limiterait l'accès à l'application à partir des seuls ordinateurs sur lesquels la clé privée est installée.

4 - D'autres utilisateurs peuvent être révoqués de cet accès donné

Cela signifie que, si nous considérons la solution de clé privée/publique, nous devons être en mesure de supprimer la clé privée qui a été donnée à l'utilisateur révoqué.

Toute suggestion sur la façon d'architecturer un tel système, ou toute idée que je pourrais m'inspirer est grandement appréciée. Merci


Mise à jour

Semble jusqu'à présent, la meilleure approche serait de crypter les données avec une clé asymétrique (je l'appelle la clé de données ), puis de crypter les partie privée de la clé de données avec une clé symétrique (qui est le mot de passe de l'utilisateur).

Cela semble une bonne solution; cependant, je peux penser à plusieurs problèmes:

  • Lorsqu'un utilisateur se connecte, son mot de passe clair doit être stocké en mémoire côté serveur pendant que la session est ouverte, car nous aurons besoin pour chaque demande de décrypter les données. Il s'agit d'une faille de sécurité car un pirate pourrait accéder à toutes les sessions ouvertes et au mot de passe de leurs utilisateurs stocké en clair.

  • Lorsque les données sont partagées (c'est-à-dire qu'un propriétaire donne accès à un invité), la clé de données est déchiffrée à l'aide du mot de passe clair du propriétaire, puis chiffrée à l'aide du mot de passe clair de l'invité. Le problème est que le propriétaire et l'invité ne sont pas nécessairement connectés en même temps, donc le serveur ne connaîtra pas le mot de passe clair de l'invité au moment où l'invitation est faite et ne pourra pas crypter les données. clé.

  • Lorsqu'un utilisateur perd son mot de passe et demande une nouvelle génération de mot de passe, il perd toutes ses données qui ne peuvent plus être déchiffrées

18
Benj

TL; DR: générer une paire de clés de données, crypter la partie privée avec la clé publique de tous les utilisateurs qui ont un accès en écriture, crypter la partie publique avec la clé publique de tous les utilisateurs qui ont un accès en lecture.


Abordons celui-ci un par un:

  1. Les données doivent être cryptées en toute sécurité dans la base de données

Il s'agit de se protéger contre les attaquants, et principalement pour que les utilisateurs sachent que même le personnel ne peut pas accéder à leurs données, les clés ne doivent donc pas être accessibles par l'équipe technique.

Compte tenu de cette exigence, la propriété la plus importante que vous devez considérer est qu'en aucun cas le serveur ne peut obtenir les informations nécessaires pour chiffrer ou déchiffrer les données. Cela implique que tout le cryptage/décryptage doit avoir lieu côté client . Étant donné que le système Web est intrinsèquement peu sûr lorsque vous devez effectuer un chiffrement de bout en bout, en raison de la capacité du serveur à injecter du code JavaScript à la demande; plus les utilisateurs soucieux de sécurité voudraient contrôler le logiciel client utilisé pour accéder au service, ils voudraient donc que cela soit implémenté comme une application de bureau.

  1. Les données sont étendues aux comptes d'utilisateurs
  2. Le propriétaire des données chiffrées doit pouvoir donner accès à ses données à d'autres utilisateurs

Ces deux contraintes signifient que plusieurs utilisateurs devront pouvoir déchiffrer les données. Cela signifie que le secret pour déchiffrer les données doit être partagé avec les autres utilisateurs.

  1. D'autres utilisateurs peuvent être révoqués de cet accès donné

Cela signifie que, si nous considérons la solution de clé privée/publique, nous devons être en mesure de supprimer la clé privée qui a été donnée à l'utilisateur révoqué.

Pour révoquer l'accès, vous devez rechiffrer les données avec une nouvelle clé. Comme d'autres réponses ont été discutées , vous ne pouvez pas imposer l'oubli.

La meilleure façon de décrire cela est peut-être à titre d'exemple.


Notations:

  • P(x) est la clé privée nommée x.
  • Q(x) est la clé publique correspondante pour x.
  • e = E(d, Q(x)) signifie que e est le résultat du chiffrement du texte en clair d avec la clé publique x.
  • d = D(e, P(x)) signifie que d est le résultat du décryptage du texte chiffré e avec la clé privée x.

Supposons qu'Alice veuille partager des données avec Bob, Charlie et Dave. Alice veut permettre à Bob de pouvoir lire et écrire les données, Charlie peut lire les données mais pas produire de données valides, et Dave peut seulement écrire mais pas décrypter ce que les autres ont écrit (il s'agit essentiellement d'un dossier de dépôt pour Dave).

Tous les utilisateurs ont des paires clé-utilisateur. P(Alice), Q(Alice) est la paire de clés utilisateur d'Alice; P(Bob), Q(Bob) est la paire de clés utilisateur de Bob; P(Charlie), Q(Charlie) est la clé utilisateur de Charlie; et P(Dave), Q(Dave) est la paire de clés utilisateur de Dave.

Le système dispose d'un registre de clé d'utilisateur où les utilisateurs peuvent partager la partie publique de leur clé d'utilisateur. La façon dont un utilisateur peut récupérer et authentifier en toute sécurité la clé d'utilisateur d'un autre utilisateur dépasse le cadre de cette réponse et est laissée à la discrétion du lecteur. La plupart des utilisateurs peuvent simplement faire confiance aux restrictions d'accès que vous placez sur votre serveur, mais les utilisateurs les plus soucieux de la sécurité devraient faire quelque chose de similaire à un signataire de clé GPG;.

Tous les utilisateurs doivent garder secret la partie privée de leur clé d'utilisateur. Comment faire cela en détail est au-delà de la portée de cette réponse, mais vous ne voulez certainement pas stocker la clé d'utilisateur privée dans le serveur non chiffrée. Au lieu de cela, ce que je suggère peut chiffrer la clé utilisateur avec une clé symétrique dérivée du mot de passe utilisateur et d'un sel, puis stocker la clé utilisateur chiffrée et le sel sur le serveur.

Pour stocker les données "Hello World" en toute sécurité, Alice commence par générer une paire clé de données : P(data), Q(data). Alice crypte ensuite les données avec la clé publique data-key :

plaintext = "Hello World"
ciphertext = E(plaintext, Q(data))

Compte tenu des propriétés de la cryptographie à clé publique, nous savons que ciphertext ne peut être déchiffré que par quelqu'un qui connaît P(data). (Notez que la notion de privé et de public pour une clé de données n'est qu'une question de convention, P(data) et Q(data) doivent être gardés privés de tous ceux qui n'en ont pas besoin, comme le serveur)

Alice veut permettre à Bob et Charlie de pouvoir lire ces données, donc Alice récupère la clé publique de Bob et Charlie Q(Bob) et Q(Charlie) et crypte P(data) avec eux, en plus de permettre Alice pour décrypter le fichier à l'avenir, éventuellement à partir d'une autre machine, Alice fait la même opération avec sa propre clé publique:

alice_read_key = E(P(data), Q(Alice))
bob_read_key = E(P(data), Q(Bob))
charlie_read_key = E(P(data), Q(Charlie))

Alice veut permettre à Bob et Dave d'être en mesure d'écrire des données qui peuvent être lues par Alice, Bob et Charlie. Alice souhaite également pouvoir mettre à jour les données à l'avenir. Pour ce faire, Alice chiffre la clé de données publique Q(data) en utilisant Q(Alice), Q(Bob) et Q(Dave):

alice_write_key = E(Q(data), Q(Alice))
bob_write_key = E(Q(data), Q(Bob))
charlie_write_key = E(Q(data), Q(Charlie))

Alice envoie ensuite tous les encrypted_key, alice_read_key, bob_read_key, charlie_read_key, alice_write_key, bob_write_key Et charlie_write_key Sur le serveur.

Étant donné que le serveur/attaquant n'est jamais en possession de P(data) ou Q(data) et que le serveur n'a pas non plus la clé privée pour déchiffrer aucun des read_keys, Le serveur ne être capable de déchiffrer ciphertext.

Lorsque Charlie veut récupérer les données, il doit télécharger à la fois ciphertext et charlie_read_key Et déchiffre charlie_read_key Avec sa clé d'utilisateur privée pour obtenir P(data) puis utilisez P(data) pour déchiffrer ciphertext:

P(data) = D(charlie_read_key, P(Charlie))
plaintext = D(ciphertext, P(data))

Charlie est maintenant en possession de plaintext. Cependant, comme Charlie n'a pas de clé d'écriture, il n'a pas de Q(data), il ne serait donc pas en mesure de mettre à jour les données dans le système d'une manière que d'autres pourraient décrypter avec succès.

Ensuite, Dave doit pouvoir compléter les données. Il ne peut pas lire le ciphertext mais il peut y ajouter en déchiffrant sa clé d'écriture pour obtenir les Q (données):

new_plaintext = "New Data"
Q(data) = D(dave_write_key, P(Dave))
new_ciphertext = E(new_plaintext, Q(data))
updated_ciphertext = ciphertext + new_ciphertext

Maintenant, Dave peut envoyer le texte mis à jour au serveur.

(Notez que dans la plupart des algorithmes de chiffrement asymétriques, vous ne pouvez pas simplement concaténer deux textes chiffrés et vous attendre à pouvoir le déchiffrer, vous devrez donc peut-être stocker des métadonnées qui maintiennent les blocs de texte chiffré séparés et les déchiffrer séparément)

Cela ne nous laisse que la révocation. Pour révoquer l'accès, vous devez disposer d'au moins P(data) pour déchiffrer le ciphertext vers plaintext, générer une nouvelle paire de clés de données: P'(data), Q'(data), et rechiffrez le texte en clair avec la nouvelle paire clé-données:

plaintext = D(ciphertext, P(data))
new_ciphertext = E(plaintext, Q'(data))

puis vous devrez mettre à jour les clés d'écriture et de lecture de chacun.

Pour ajouter un nouvel utilisateur à un fichier existant, il vous suffit de créer sa clé d'écriture et sa clé de lecture. Seules les personnes qui peuvent décrypter leur clé de lecture peuvent étendre une clé de lecture à un nouvel utilisateur, et seules les personnes qui peuvent décrypter leur clé d'écriture peuvent étendre une clé d'écriture à un nouvel utilisateur.


Si vous n'avez pas besoin de l'autorisation d'autorisation détaillée dans ce système, (IOW, si tous les utilisateurs qui peuvent lire les données peuvent également les mettre à jour); ou si vous utilisez d'autres moyens pour appliquer des autorisations précises, vous pouvez remplacer la clé de données asymétrique par une clé de données symétrique ( Anecdote : le système avec clé de données symétrique serait similaire à la façon dont fonctionne le courrier électronique crypté PGP à plusieurs destinataires; vous pouvez donc vouloir étudier cela).

14
Lie Ryan

La méthodologie générique pour ce type de problème raisonne en termes de connaissance et indirection.

Vous voulez que chaque utilisateur puisse faire certaines choses que les autres utilisateurs, ou les "techniciens", ne peuvent pas faire; par conséquent, chaque utilisateur doit connaître une valeur secrète que les autres personnes n'ont pas. Le mot de passe de l'utilisateur peut être un tel secret; sinon, vous auriez besoin de quelque chose stocké côté client.

L'accès à chaque élément de données doit être accessible uniquement à un ensemble sélectionné de personnes à tout moment, de sorte que les données doivent être cryptées et la clé de cryptage connue de ces personnes exactement. De plus, vous voulez pouvoir partager des éléments par élément, donc chaque élément (fichier) devra avoir sa propre clé de chiffrement.

Vous ne pouvez pas imposer l'oubli; si quelqu'un connaissait, à un moment donné, le contenu d'un fichier, alors vous ne pouvez pas le faire pour qu'il l'oublie. En termes pratiques, ils peuvent avoir effectué une sauvegarde sur leur propre machine. Par conséquent, vous ne pouvez pas révoquer l'accès à un élément de données. Au mieux, vous pouvez choisir par fichier qui peut le lire, et donc ne pas mettre à la disposition de certaines personnes la nouvelle version d'un fichier donné.

Puisque vous voulez que les utilisateurs se donnent accès à certains fichiers entre eux, vous avez besoin d'une sorte de rendez-vous, qui sera plus facilement réalisé avec la cryptographie asymétrique.


Cela conduit à la conception suivante:

  • Chaque utilisateur [[# #]] u [~ # ~] possède une paire de clés publique/privée P U / S U  d'un type adapté au chiffrement asymétrique (par exemple, RSA).

  • La clé privée est stockée "quelque part" de telle sorte que seul le propriétaire légitime puisse y accéder. Une méthode serait le cryptage de la clé privée avec le mot de passe de l'utilisateur (en supposant que l'utilisateur n'envoie jamais son mot de passe à votre serveur, sinon les "techniciens" pourraient le saisir). Alternativement, la clé privée de l'utilisateur est stockée dans un fichier sur son système de bureau/ordinateur portable.

  • Chaque élément de données (ou fichier) est crypté avec sa propre clé générée de manière aléatoire K (cryptage symétrique).

  • Avec chaque fichier, sont stockées des versions cryptées de K avec les clés publiques des utilisateurs qui devraient pouvoir lire le fichier. Si l'utilisateur U fait partie de cet ensemble, alors cet utilisateur utilise sa clé privée S = U  pour récupérer K et décrypter le fichier.

  • Le partage d'un fichier avec un autre utilisateur V se fait en récupérant K , puis en cryptant - K avec P V  (la clé publique de l'utilisateur V ) et stocker le résultat le long du fichier (ou le mettre à la disposition de l'utilisateur V par un autre mécanisme).

  • Si un utilisateur change son mot de passe, cela a un impact, tout au plus, sur le stockage de sa clé privée. Rien à voir avec les fichiers. Bien que le mot de passe de l'utilisateur puisse changer, sa paire de clés publique/privée est permanente.

  • Lorsqu'un fichier est modifié, vous pouvez soit traiter la nouvelle version comme un nouveau fichier indépendant, avec sa propre nouvelle clé K et son propre ensemble de destinataires. Si le nouvel ensemble de destinataires est identique à l'ancien ensemble (ou à un sur-ensemble de celui-ci), vous pouvez simplement réutiliser la même clé K , ce qui peut être plus simple pour la mise en œuvre. Changer la clé K est ce qui ressemble le plus à la "révocation de l'accès" (sous réserve de la mise en garde de l'oubli inapplicable).


Bien entendu, les "techniciens" contrôlent toujours le logiciel qui est effectué pour effectuer ces opérations (en particulier dans un contexte Web, le Javascript étant envoyé par le serveur lui-même, ou si les opérations de chiffrement/déchiffrement se font côté serveur), donc s'ils vraiment veulent tromper les utilisateurs, alors on doit supposer qu'ils le peuvent.

3
Tom Leek

C'est un problème intéressant, mais il a en fait été résolu dans diverses applications open source à ce stade. Je recommanderais, pour votre cas d'utilisation, d'emprunter au modèle de chiffrement d'ownCloud (qui a l'avantage d'être open-source).

L'application générale de ce modèle sur votre logiciel devrait ressembler à:

1) Bien sûr, cela peut être fait de plusieurs manières, mais je recommande que le serveur d'applications lui-même crypte ces données en utilisant un cryptage asymétrique (clé publique-privée), puis un cryptage symétrique. Il y a beaucoup de choses que vous pouvez faire avec le chiffrement symétrique - comme avoir la moitié de la clé sur le serveur et obliger l'utilisateur à fournir l'autre moitié, etc. pour résoudre ce problème.

2) Comme le souligne o11c, le cryptage de la clé privée asymétrique avec une méthode de cryptage symétrique (mot de passe) résoudra définitivement ce problème.

3) Lorsque d'autres utilisateurs ont besoin d'une copie des données, vous devez faire déchiffrer le serveur d'applications, puis rechiffrer les données pour cet utilisateur. De cette façon, vous vous retrouvez avec des doublons de données pour chaque utilisateur qui en a besoin. La méthode ownCloud est intéressante - elle utilise une "clé de partage" asymétrique pour crypter les fichiers qu'un utilisateur partage. Cette clé de partage est générée pour chaque fichier et utilisateur avec lequel le fichier est partagé. Vous pouvez ensuite demander au serveur d'applications de déchiffrer les données, de les chiffrer avec la clé publique de cet utilisateur, puis seul le mot de passe de cet utilisateur déverrouillera la clé privée nécessaire pour déchiffrer le fichier.

4) En vous basant sur 3, tout ce que vous devez faire est de supprimer la clé de partage nouvellement générée et l'accès est révoqué en toute sécurité (à condition qu'ils n'aient pas fait quelque chose comme le télécharger ou effectuer une capture d'écran, etc.).

1
Herringbone Cat

Apple utilise un tel mécanisme sur iCloud. Je crois que c'est ainsi que cela fonctionne (si ma mémoire est bonne) et légèrement différent de ce que d'autres ont suggéré. Pour autant que je le comprends, il s'agit uniquement d'un cryptage asymétrique.

1) L'appareil (iPhone, iPad, etc.) génère une paire de clés (clé d'appareil).

2) Pour un nouveau compte iCloud, l'appareil génère une deuxième paire de clés (la clé de cryptage).

3) Le périphérique crypte la partie privée de la clé de cryptage à l'aide de la clé publique du périphérique. La clé de chiffrement publique (texte en clair) et la clé de chiffrement privée (chiffrée) sont stockées sur le serveur.

4) L'appareil utilise la clé de cryptage publique pour crypter les données envoyées au serveur.

Pour partager des données:

1) Vous avez besoin d'un appareil déjà connecté au cloud. Appelons ce périphérique 1. Le nouveau périphérique est le périphérique 2. 2) Le périphérique 2 génère sa propre paire de clés de périphérique. 3) Le périphérique 2 envoie sa clé publique au périphérique 1 (directement ou via le cloud. Directement est plus sécurisé). 4) Le périphérique 1 déchiffre la clé privée de chiffrement à l'aide de sa propre clé privée, puis la chiffre à l'aide de la clé publique du périphérique 2.

Il pourrait y avoir une vulnérabilité potentielle à l'étape 3; si un attaquant peut inciter le périphérique 1 à accepter sa clé publique, il pourrait avoir accès aux données partagées. Je ne sais pas comment cela est résolu, mais cela implique probablement l'identification de l'appareil et les empreintes digitales clés.

Modifier pour clarification: la paire de clés de chiffrement dans ma description serait par utilisateur, mais vous pouvez utiliser le même mécanisme sur une portée différente. La portée détermine "l'unité de partage" - si vous voulez pouvoir décider de partager ou de ne pas partager des fichiers individuels, alors chaque fichier devra avoir sa propre paire de clés. Pour le partage, seule la paire de clés, et non les données sous-jacentes, serait dupliquée.

1
Kevin Keane