# Démarrage rapide
Pour démarrer rapidement, nous allons intégrer les fonctionnalités de chiffrement & déchiffrement directement dans le projet exemple, en protégeant l'identité par mot de passe.
La branche sur laquelle est basée cette étape est
master
(opens new window), le
résultat final est 1-quick-start
(opens new window).
# Installation
Nous allons utiliser la version "web" du SDK @seald-io/sdk
avec le module
de protection de l'identité par mot de passe
@seald-io/sdk-plugin-ssks-password
:
cd frontend
npm install @seald-io/sdk @seald-io/sdk-plugin-ssks-password
Pour le moment, il n'y a aucune modification à apporter à la partie backend.
# Configuration
Pour configurer le SDK, il faut se connecter à votre tableau d'administration et récupérer les 3 éléments suivants :
appId
: UUID unique pour votre application ;JWT_SECRET
: secret de JWT ;JWT_SECRET_ID
: UUID associé au secret correspondant àJWTSecret
utilisée pour générer des JWT.apiURL
: URL du serveur d'API à utiliser ;keyStorageURL
: URL du serveur de stockage des identités à utiliser.
TIP
Les secrets de JWT peuvent être généré dans les paramètres, onglet secret de JWT
.
Pour plus d'information référez-vous à cette documentation.
TIP
Pour créer un compte développeur, suivez les premiers pas.
Pour instancier le SDK, on procède comme suit :
TIP
Étant donné que l'instance du SDK est partagée à travers toute une application
en React, il est préférable de la stocker dans une variable dans un fichier de
service seald.js
et d'exposer un getter plutôt que de l'enregistrer dans une
variable immutable.
# Gestion des identités Seald
Lors de la création d'une identité Seald, il faut l'associer d'une part au
compte développeur, et d'autre part à l'utilisateur (dont l'identifiant est noté
userId
) dans l'application dans laquelle on intègre le SDK.
Pour cela, il faut générer un JSON Web Token appelé signupJWT
à partir du JWT_SECRET
, et du JWT_SECRET_ID
.
# Génération d'un signupJWT
La génération d'un signupJWT
se fait hors ligne, et est décrite
dans ce guide dédié.
Cette opération doit être effectuée depuis le backend, mais dans un premier temps nous effectuerons cette opération côté frontend pour arriver plus vite à un prototype fonctionnel. Nous allons utiliser un utilitaire fourni par le SDK pour générer simplement des JWT.
WARNING
En production, il ne faut pas donner au frontend la connaissance de JWTSecret
.
Si c'était le cas, elle pourrait être utilisée par un attaquant
pour créer de nouveaux comptes associés à votre compte développeur, dépenser vos
licences utilisateur, et possiblement réassigner les userIDs de vos utilisateurs
à des comptes Seald qu'ils contrôlent.
Vous pouvez vous référer au guide dédié aux JWT.
# Création d'une identité
Une fois le jeton généré, on peut créer une nouvelle identité de la façon suivante :
Une fois cette fonction exécutée, l'instance du SDK est prête pour chiffrer et déchiffrer, et les clés propres à l'identité sont en mémoire.
# Protection de l'identité par mot de passe
Une fois l'identité créée, elle n'existe qu'en mémoire, il faut la sauvegarder pour pouvoir l'utiliser dans une session ultérieure.
Pour cela nous allons utiliser le module @seald-io/sdk-plugin-ssks-password
qui permet de protéger les clés d'identité par un mot de passe password
.
Une fois cette fonction exécutée, l'identité est sauvegardée et pourra être
récupérée en utilisant la fonction équivalente sealdSDKInstance.ssksPassword.retrieveIdentity
depuis une instance nouvellement instanciée.
Pour plus de détails sur ce mécanisme, référez-vous au guide dédié à la protection des identités par mot de passe.
TIP
Pour une exécution plus rapide, dans le cadre d'une utilisation avancée du SDK, vous pouvez personnaliser la dérivation du mot de passe en une rawEncryptionKey
et une rawStorageKey
, et passer celles-ci à la place du mot de passe à ssksPassword.saveIdentity
. Pour plus de détails, allez voir le paragraphe " Personnaliser la dérivation de mot de passe" du guide sur les identités.
# Exposer des fonctions createIdentity
et retrieveIdentity
Si on rassemble tous les éléments étudiés dans les parties précédentes, on peut exposer deux fonctions :
# Intégrer à la création de compte et à la connexion
L'étape suivante consiste à appeler ces fonctions lors de la création du compte dans l'application et lors de la connexion.
Pour la créer lors de la création de compte :
Pour la récupérer à la connexion :
Comme l'identité n'est conservée qu'en mémoire dans le navigateur, si vous ouvrez un nouvel onglet, vous devrez retaper le mot de passe. Pour contourner ce problème, nous n'essayons pas d'obtenir du serveur le profil de l'utilisateur connecté :
Nous verrons plus tard comment faire persister cette identité dans le navigateur.
DANGER
Nous mettons ici de côté un souci majeur de sécurité : le mot de passe d'authentification ne doit pas être utilisé tel quel pour protéger l'identité. Il faut modifier la méthode d'authentification (avec une pré-dérivation du mot de passe) pour que le serveur applicatif ne puisse pas avoir connaissance du mot de passe. Un guide dédié est disponible ici.
# Chiffrer & déchiffrer des messages
Pour effectuer des chiffrements et déchiffrements de messages, il faut :
- créer une session de chiffrement partagée entre les destinataires ;
- chiffrer les messages avant envoi ;
- déchiffrer les messages après réception.
# Session de chiffrement
Pour créer une session de chiffrement partagée entre les utilisateurs userId_1
et userId_2
dans le salon de chat roomId_1
, on procède de la façon suivante :
Cela renvoie une EncryptionSession
avec un nouveau sessionId
unique attribué par le serveur.
Dans le cadre de ce projet, on place dans metadata
l'identifiant du salon de chat roomId
.
Pour chiffrer un message avec une session, on procède comme suit :
Cela va donner un string de la forme :
'{"sessionId":"0000000000000000000000","data":"8RwaOppCD3uIJVFv2LoP3XGXpomj0xsMv4qmMVy30Vdqor2w0+DVCXu3j13PEyN2KfJm6SiSrWDRMDziiiOUjQ=="}'
On peut récupérer une instance de la session, soit avec le sessionId
, soit
directement avec le message chiffré :
Une fois la session récupérée, on peut l'utiliser pour déchiffrer un message :
On peut aussi ajouter / révoquer des destinataires de la session :
Pour plus de détails sur les sessions, vous pouvez consulter le guide dédié.
# Intégration dans un salon de chat
Pour intégrer ces fonctions dans le chat, il faut modifier trois éléments :
- l'envoi des messages, qui doit désormais chiffrer pour les destinataires du salon ;
- la réception de messages, qui doit désormais déchiffrer ;
- la gestion des membres d'un salon, qui doit désormais transposer les membres d'un salon en droits cryptographiques.
# Chiffrement à l'envoi
Il faut appeler la fonction encrypt
à chaque fois qu'un message est posté.
Dans le fichier Chat.jsx
se trouve une fonction handleSumbitMessage
que l'on
va modifier pour y incorporer du chiffrement :
Par ailleurs, lors de la création d'un salon de chat multi-utilisateurs dans
ManageDialogRoom.jsx
un message 'Hello 👋'
est envoyé. Il faut le chiffrer :
# Déchiffrement à la réception
Il faut appeler la fonction decrypt
à chaque fois qu'un message est collecté.
Pour cela on va créer quelques fonctions helpers dans Chat.jsx
qui serviront à
utiliser le même modèle de données partout :
On va modifier l'eventListener
de l'évènement room:messageSent
:
On va modifier la récupération de l'historique des messages au chargement d'un salon :
# Modifier les droits à l'édition d'un salon
Lorsque le salon est multi-utilisateurs, le créateur du salon peut ajouter / supprimer des membres. Les sessions permettent de gérer ces mouvements.
Pour cela il faut au moment où le créateur du salon effectue ces mouvements sur le salon, effectuer les mêmes mouvements sur la session de chiffrement.
Lorsque l'on déclenche la modification d'un salon, il faut donner la session
associée au salon lors du dispatch
:
Dans ManageDialogRoom.jsx
avant de faire l'appel API au serveur pour
effectivement éditer le salon :
# Conclusion
Nous avons pu intégrer rapidement du chiffrement dans ce projet, restent en suspens 3 sujets avant de passer en production :
- la pré-dérivation du mot de passe ;
- la persistance de l'identité à l'ouverture d'un nouvel onglet en stockant l'identité en localstorage ;
- la génération des jetons de licence depuis le backend.