Aller au contenu

Étude de casPRIVY.SYS2025

Privy, messagerie chiffrée de bout en bout

Ce que ce projet, audité sans complaisance, m'a appris sur la sécurité et l'architecture.

  • 9failles majeuresaudit de juillet 2026
  • 7catégoriessécurité, performance, réseau…
  • 8correctionsce que je ferais aujourd'hui
[ POST_MORTEM.LOG ]ARCHIVÉ • POC ÉDUCATIF

[01]Contexte

Privy est né de l'envie de concevoir une messagerie instantanée confidentielle, sans dépendre d'un géant centralisé. L'objectif était le principe de divulgation nulle de connaissances : les serveurs ne devaient être que des relais aveugles de paquets chiffrés, sans jamais pouvoir lire les conversations ni accéder aux clés symétriques.

Fonctions : inscription par pseudonyme avec génération de la paire de clés sur l'appareil, invitations sécurisées, échanges en temps réel avec indicateurs de frappe, application Android via Capacitor et ébauche de partage de fichiers.

[02]Architecture

Client Vue 3 / Vite avec Pinia, encapsulé en application Android via Capacitor. Côté serveur : une API REST Express (authentification JWT, profils, invitations), un serveur WebSocket (ws) pour le relais des messages chiffrés et MySQL 5.7, le tout orchestré avec Docker.

  1. 1. Identité

    À l'inscription, le navigateur génère une paire RSA-OAEP 2048 bits (SHA-256) via l'API Web Crypto. La clé publique est envoyée à l'API et stockée côté serveur.

  2. 2. Enveloppe

    L'initiateur génère une clé AES-GCM 256 bits, la chiffre avec la clé publique du destinataire et l'envoie dans une invitation. Le destinataire la déchiffre avec sa clé privée locale.

  3. 3. Messages

    Chaque message est chiffré localement en AES-GCM, avec un IV aléatoire de 12 octets placé devant le texte chiffré, puis encodé en Base64. Le serveur WebSocket ne reçoit qu'un blob opaque.

Flux cryptographique : points faibles (!) et correctifs (→)APPAREIL ARSA-OAEP 2048AES-GCM 256! clé privée enclair→ IndexedDB nonextractibleSERVEURAPI RESTWebSocketrelais aveugle! HTTP toutes les30 s→ JWT validélocalementAPPAREIL BRSA-OAEP 2048AES-GCM 2561 clépublique2 enveloppe RSA(AES)3 AES-GCM+ IV,Base64point faiblecorrectif
Le flux de chiffrement. Les points faibles sont marqués « ! » (en rose), les correctifs « → » (en orange).

[03]Ce que l'audit a révélé

Voici les 9 failles majeures relevées par l'audit, avec le constat et son impact.

9 failles affichées

  1. FAILLE 01 • SÉCURITÉ

    Clé privée RSA en clair dans le localStorage

    La clé privée RSA et les clés de conversation sont écrites en clair dans le localStorage : une seule faille XSS suffit à les extraire et à anéantir le chiffrement de bout en bout.

  2. FAILLE 02 • PERFORMANCE

    pbkdf2Sync bloque l'Event Loop Node.js

    Le hachage des mots de passe utilise pbkdf2Sync (1 000 itérations), qui mobilise le thread unique de Node.js à chaque appel : sous charge, l'API ne répond plus.

  3. FAILLE 03 • RÉSEAU

    Revalidation HTTP des sessions toutes les 30 s

    Le serveur WebSocket n'a accès ni à la base ni à un cache partagé : il revalide chaque session par une requête HTTP toutes les 30 s, soit environ 2 000 requêtes par minute pour 1 000 clients, pour vérifier des jetons déjà signés.

  4. FAILLE 04 • ARCHITECTURE

    Modèle relationnel limité au 1 contre 1

    La table Conversations repose sur deux colonnes creator_id et participant_id : les salons de groupe sont impossibles sans reconstruire le schéma et réécrire les contrôleurs.

  5. FAILLE 05 • PERFORMANCE

    Déconnexion en O(R) sur le nombre de salons

    À la déconnexion, le serveur WebSocket parcourt tous les salons en mémoire pour détacher le socket : un coût linéaire au lieu d'un coût constant.

  6. FAILLE 06 • BASE DE DONNÉES

    Fichiers découpés en blocs de 255 octets dans MySQL

    Un fichier de 5 Mo aurait demandé plus de 20 000 lignes en base. La fonctionnalité a été abandonnée en cours de développement et ses routes désactivées.

  7. FAILLE 07 • CONFORMITÉ

    Permissions Android sur-provisionnées

    Le manifeste déclarait la localisation précise, la caméra et le micro, ajoutés lors d'essais pour résoudre un problème d'accès au stockage : une violation du moindre privilège, qui aurait provoqué un rejet à la soumission sur le Google Play Store.

  8. FAILLE 08 • FIABILITÉ

    Jeton périmé et reconnexion infinie

    Le jeton JWT n'était lu qu'une fois, à l'évaluation du module : après la connexion, les autres composables gardaient une valeur nulle et provoquaient des erreurs 401 jusqu'au rechargement de la page. Le WebSocket tentait aussi de se reconnecter toutes les 5 s après une déconnexion volontaire.

  9. FAILLE 09 • SÉCURITÉ

    Secrets versionnés, valeurs triviales

    Les fichiers .env sont suivis par Git depuis le premier commit, avec des valeurs triviales. Déployée telle quelle, cette configuration aurait permis de forger des jetons valides pour n'importe quel utilisateur. Je les ai volontairement laissés en place dans la rétrospective, comme exemple visible d'une erreur que je ne commets plus.

[04]Ce que je ferais aujourd'hui

Si Privy devait être réécrit selon les standards actuels, voici ce que je changerais.

Ce que je ferais aujourd'hui
ÉlémentAvant (PoC)Après (cible)
ClésAvant (PoC) Clé privée en clair dans localStorageAprès (cible)Clés non extractibles dans IndexedDB
Mots de passeAvant (PoC) pbkdf2Sync bloquantAprès (cible)Argon2id asynchrone
Session WebSocketAvant (PoC) Requête HTTP toutes les 30 sAprès (cible)Validation locale du JWT
SalonsAvant (PoC) Colonnes creator / participant (1 contre 1)Après (cible)Table ConversationMembers
FichiersAvant (PoC) Micro-blocs de 255 octets dans MySQLAprès (cible)Stockage objet S3 / MinIO
État du frontAvant (PoC) Variables globales de modules JSAprès (cible)Store Pinia, intercepteurs Axios centralisés
Permissions AndroidAvant (PoC) GPS, caméra, micro déclarésAprès (cible)INTERNET seul, demandes au runtime
SecretsAvant (PoC) .env versionnés, valeurs trivialesAprès (cible).env.example, secrets forts injectés, détection en CI

[05]Ce que ça a déclenché

La certification CCNA, le durcissement de ce portfolio (CSP avec nonce, déploiement signé HMAC, isolation réseau) et la veille CVE intégrée au site.

[06]Limites

  • Le projet est archivé : c'est un PoC éducatif, qui ne doit jamais être déployé en production.
  • Il repose sur des dépendances obsolètes (dont MySQL 5.7, en fin de vie) et présente les failles décrites ci-dessus. Les valeurs des .env versionnés sont publiques.
// Dernière étude de casRetour aux projetsTous les projets sont sur l'accueil

Parlons-en

Une question sur ce travail, ou envie d'en voir plus ? Écrivez-moi.

Me contacter