# Intégrer le 2-man-rule sur votre backend
Comme expliqué dans le guide de gestion des identités la protection en 2-man rule nécessite quelques modifications sur votre backend.
Plus précisément, deux choses doivent être modifiées :
- la génération et stockage d'une clé
twoManRuleKey
(ourawTwoManRuleKey
) secrète associée à un utilisateur de votre application, remise lors de l'authentification ; - une interaction avec le service Seald SDK Key Storage (SSKS) pour que l'utilisateur puisse s'authentifier auprès de SSKS.
On notera :
- Utilisateur : l'utilisateur de l'application dont on veut protéger l'identité ;
- Identité : une identité cryptographique Seald de l'Utilisateur (décrite dans le guide dédié) ;
- Frontend : le frontend de l'application dans laquelle le SDK est intégré ;
- Backend : le backend de l'application dans laquelle le SDK est
intégré, il stockera
twoManRuleKey
, la clé permettant de déchiffrerencryptedIdentity
; - SSKS : une instance de Seald SDK Key Storage, il stockera
encryptedIdentity
non déchiffrable sans latwoManRuleKey
; - Facteur d'authentification : le moyen d'authentifiaction utilisé par l'utilisateur sur SSKS. Actuellement l'authentification par email et par SMS sont disponible ;
L'objectif est de donner à l'Utilisateur les deux "morceaux" de son identité
Seald (twoManRuleKey
et encryptedIdentity
) par authentification, tout en
empêchant SSKS et le Backend de pouvoir chacun indépendamment accéder
à l'identité de l'utilisateur.
TIP
Lors de la protection de l'identité en 2-man rule, vous pouvez utiliser soit
l'argumenttwoManRuleKey
, soit l'argument rawTwoManRuleKey
, pour chiffrer
l'identité stockée sur SSKS.
Les deux sont très similaires à la fois sur l'idée et sur la manière de les
utiliser, la seule différence étant que twoManRuleKey
est d'un format libre
(mais avoir un aléa suffisant est toujours important), alors que
rawTwoManRuleKey
doit absolument être l'encodage en base64 d'un buffer
cryptographiquement aléatoire de 64 octets.
Techniquement, cela évite de dériver la twoManRuleKey
avec scrypt
, donc
utiliser rawTwoManRuleKey
rend le stockage et la récupération de l'identité un
peu plus rapides, mais twoManRuleKey
peut être un peu plus facile à utiliser.
Pour plus de détails, voir le paragraphe dédié du guide sur les identités.
Dans ce guide, nous dirons twoManRuleKey
seulement, mais pour évoquer l'une
des deux.
# Préalable
Pour communiquer avec le serveur SSKS, votre Backend aura besoin de :
keyStorageURL
: l'URL du serveur SSKS, elle peut être récupérée sur le tableau d'administration ;ssksAppId
: correspond auappId
utilisé pour le SDK ;ssksAppKey
: permet à votre Backend de s'authentifier auprès du serveur SSKS.
Cette ssksAppKey
peut être générée ou renouvelée sur le tableau d'administration.
![]() |
1. Aller sur la page de gestion du SDK |
![]() |
2. Dans la partie "Clé serveur SSKS-2MR", cliquer sur le bouton "Générer la clé" |
![]() |
3. Vous pouvez copier la clé générée. Attention, vous n'y aurez plus accès plus tard ! Enregistrez-la maintenant ! |
# Protocole
Le protocole s'effectue en deux étapes :
- la protection initiale de l'identité qui n'arrive normalement qu'une seule fois immédiatement après sa génération ;
- la récupération de l'identité qui arrive à chaque fois que l'Utilisateur veut récupérer son identité localement.
TIP
La protection d'une identité en 2-man rule est usuellement couplée à une base de données locale persistante pour "mettre en cache" l'identité de l'Utilisateur et éviter qu'il ait à nouveau besoin de la récupérer à chaque fois qu'il ouvre l'application.
Le protocole pour effectuer la protection initiale d'une identité notée
identity
de l'Utilisateur est le suivant :
TIP
Dans le cas où une identité a déjà été stockée sur SSKS avec ce facteur d'authentification,
ou si la requête contenait force_auth: true
,
le serveur SSKS demandera alors une authentification par challenge, en envoyant
mustAuthenticate
à true
.
L'utilisateur recevra un challenge
par email ou SMS, valable 6h, et devra le répéter
dans le front-end pour pouvoir s'authentifier sur SSKS avant de stocker sa nouvelle
identité.
Dans le cas où aucune identité n'a jamais été stockée sur SSKS avec ce facteur
d'authentification, le serveur répondra au contraire avec
mustAuthenticate: false
(sauf si la requête contenait force_auth: true
).
L'utilisateur ne recevera alors aucun challenge
, et peut directement stocker
son identité.
Pour récupérer l'identité identity
de cet Utilisateur lors d'une autre session,
le protocole est le suivant :
Les flèches rouges dans le schéma indiquent les opérations à implémenter dans
l'application utilisant le SDK Seald et le mode de protection en 2-man rule. Les
flèches noires indiquent ce qui est déjà implémenté par le SDK, le plugin de
protection en 2-man rule @seald-io/sdk-plugin-ssks-2mr
ou SSKS.
WARNING
Les adresses emails et numéros de téléphones envoyés au serveur SSKS doivent absolument être normalisés.
# Protection de l'identité
Avant d'appeler la méthode saveIdentity
exposée par le plugin
@seald-io/sdk-plugin-ssks-2mr
,
il faut les éléments suivants :
userId
: un identifiant unique de l'utilisateur dans l'application ;authFactor
: adresse e-mail ou numéro de telephone de l'utilisateur utilisée pour l'authentification par challenge, doit être répété ou vérifié par l'utilisateur pour s'assurer que le backend n'essaye pas de réaliser une attaque MITM (opens new window) ;twoManRuleKey
: la clé connue du backend permettant de chiffrer / déchiffrer l'identité ;sessionId
: l'identifiant de session généré par SSKS et transmis par le backend au SDK ;- si le serveur à envoyé
mustAuthenticate
àtrue
,challenge
: un jeton aléatoire envoyé par SSKS par e-mail ou SMS à l'utilisateur pour l'authentifier, valable 6h, ne doit jamais être révélé au backend ;
Pour cela, il faut implémenter un point d'API getSSKSSession
authentifié sur
le backend qui :
- utilise le point d'API
POST https://{SSKS_URL}/tmr/back/challenge_send/
pour envoyer à SSKS :
create_user
: donnertrue
pour créer en même temps l'utilisateur sur SSKS sinon cela devra être fait avec une requête préaable ;user_id
: correspond auuserId
dans@seald-io/sdk-plugin-ssks-2mr
;auth_factor
: un object contenant deux entrées:type
: le type de facteur d'authentification. 'EM' pour un email, ou 'SMS' pour un numéro de téléphone ;value
: l'adresse e-mail ou le numéro de téléphone de l'utilisateur où lechallenge
sera envoyé ;
template
: le template d'email à utiliser au format HTML, ou le contenu du SMS, et qui doit contenir$$CHALLENGE$$
qui sera remplacé par lechallenge
.SSKS va retourner au backend
sessionId
etmustAuthenticate
.Si
mustAuthenticate
esttrue
, SSKS va envoyer un email ou un SMS au format detemplate
au facteur d'authentification contenantchallenge
, valable 6h ;
- génère
twoManRuleKey
(64 octets aléatoires robustes cryptographiquement) et la stocke associé à cet utilisateur ;
WARNING
Dans le cas où vous voulez utiliser une rawTwoManRuleKey
, celle-ci doit
absolument être l'encodage en base64 d'un buffer cryptographiquement aléatoire
de 64 octets.
Si vous utiliser une twoManRuleKey
simple, le format est libre, mais celle-ci
doit quand même contenir suffisamment d'aléa.
- renvoie au frontend
sessionId
,mustAuthenticate
, ettwoManRuleKey
.
TIP
Dans le cas où mustAuthenticate
est true
, le frontend doit bloquer et attendre que l'utilisateur entre le challenge qui lui a été
envoyé avant d'appeler la méthode saveIdentity
.
Dans le cas où mustAuthenticate
est false
, le frontend peut appeler la méthode saveIdentity
directement.
Ensuite, le frontend peut utiliser la méthode saveIdentity
de la façon suivante :
TIP
Pour une sécurité optimale, il est recommandé d'afficher en frontend le facteur d'authentification
qui va être envoyée à SSKS avant d'appeler le saveIdentity
,
afin de s'assurer que le backend a bien créé l'utilisateur SSKS avec la bonne
valeur, et qu'on va bien stocker l'identité de l'utilisateur associée à
un facteur d'authentification qu'il maitrise.
# Récupération de l'identité
Avant d'appeler la méthode retrieveIdentity
exposée par le plugin
@seald-io/sdk- plugin-ssks-2mr
,
il faut les mêmes éléments que pour la méthode saveIdentity
décrite
au paragraphe précédent.
Pour cela, on réutilise le même point getSSKSSession
décrit à la partie
précédente, sauf qu'il ne génère pas une nouvelle twoManRuleKey
mais renvoie celle déjà
générée :
- utilise le point d'API
POST https://{SSKS_URL}/tmr/back/challenge_send/
pour envoyer à SSKS :
create_user
: donnerFalse
, à cette étape l'utilisateur est déjà crééuser_id
: correspond auuserId
dans@seald-io/sdk-plugin-ssks-2mr
;auth_factor
: un object contenant deux entrées:type
: le type de facteur d'authentification. 'EM' pour un email, ou 'SMS' pour un numéro de téléphone ;value
: l'adresse e-mail ou le numéro de téléphone de l'utilisateur où lechallenge
sera envoyé ;
force_auth
: si vous voulez forcer l'authentification de l'utilisateur, même si il n'a jamais stocké d'identité avec ceauth_factor
, c'est à dire forcer le serveur à mettremustAuthenticate
àtrue
;subject
: (dans le cas d'un email) la ligne d'objet de l'email à utiliser ;template
: le template d'email à utiliser au format HTML, ou le contenu du SMS, et qui doit contenir$$CHALLENGE$$
qui sera remplacé par lechallenge
.SSKS va envoyer un email ou un SMS au format de
template
au facteur d'authentification contenantchallenge
, valable 6h, et retourner au backendsessionId
;
- récupère la
twoManRuleKey
stockée associée à cet utilisateur (différent de la partie précédente) ; - renvoie au frontend
sessionId
ettwoManRuleKey
.
Ensuite, le frontend peut utiliser la méthode retrieveIdentity
de la façon suivante :
WARNING
Il est déconseillé de récupérer la même identité avec ssks2MR.retrieveIdentity
sur plusieurs appareils en même temps, au même instant exact, par exemple lors
de tests automatisés. Veuillez attendre que l'un des appareils ait fini de
récupérer l'identité avant de lancer la récupération sur un autre appareil.
# Environnement de test
En environnement de test, votre serveur peut, lors de l'appel à
POST https://{SSKS_URL}/tmr/back/challenge_send/
,
rajouter un argument fake_otp: true
. Ceci aura pour effet de ne pas envoyer
réellement de challenge par email ou SMS. Le challenge sera alors fixé à
'aaaaaaaa'
.
Vous pouvez utiliser ceci pour accélérer votre développement local, pour des tests automatisés, ...
WARNING
Faites bien attention de ne pas envoyer fake_otp: true
en environnement de
production. Le serveur vous répondrait avec une erreur 406 Not Acceptable
.
# Sécurité
La sécurité d'un tel protocole se fonde sur :
- le stockage sécurisé d'au moins un des morceaux de l'identité : contrairement à un stockage chez un tiers de confiance unique (coffre-fort numérique, KMS, Cloud HSM, ...), ce protocole est robuste à une compromission partielle. Il faudrait compromettre Backend et SSKS simultanément pour compromettre les identités des utilisateurs.
- l'indépendance des authentifications : il faut que SSKS et le Backend ne reposent pas sur les mêmes protocoles d'authentification. Si tous deux utilisent le même SSO par exemple, il suffit alors que le serveur de SSO devienne malveillant pour compromettre totalement la sécurité du protocole. Il en va de même pour les social logins.
- non-collusion des serveurs : il faut que SSKS et le Backend ne puissent pas avoir accès à l'autre secret, et surtout pas par API, sinon il suffit de compromettre un serveur et son secret pour obtenir l'autre secret.
- le "découpage" sécurisé de l'identité : le protocole choisi par Seald pour "découper" l'identité fait que connaître un "morceau" n'aide pas à accélérer une attaque par force brute, contrairement à un découpage naïf d'un string en deux.
Les limites de ce protocole sont les suivantes :
- si les deux protocoles utilisent les mêmes facteurs pour authentifier l'utilisateur (par exemple l'email), il suffit alors de compromettre le facteur de l'utilisateur pour compromettre l'identité (comme pour le SSO).
- si SSKS et Backend étaient compromis (ou perquisitionnés) simultanément, les identités des utilisateurs seraient reconstituées par l'attaquant.
WARNING
Ce protocole n'est pas strictement "de bout-en-bout", dans la mesure où l'identité de l'utilisateur peut être reconstituée sans son intervention ou l'intervention de ses appareils.
# Exemples
Les fonctions à implémenter étant relativement simples, et leur implémentation variant considérablement d'une technologie de backend à une autre, nous ne fournissons pas de bibliothèque implémentant ces fonctions.
En revanche, cette partie regroupe plusieurs exemples d'implémentation dans plusieurs langages.
# PHP (vanilla)
get_ssks_session.php
:
# Python (django)
views.py
: