Sécurité de Cyora

Modèle de menace, algorithmes utilisés, et comment vérifier toi-même que les promesses de confidentialité sont respectées.

Modèle de menace

Le serveur de stockage de blobs Cyora (Supabase) est considéré comme honest-but-curious : on ne lui fait pas confiance pour respecter la confidentialité du contenu, mais on suppose qu'il ne supprime pas les blobs aléatoirement.

Conséquence : les attaquants externes qui prendraient le contrôle du serveur (par exploit, fuite de credentials, ordre judiciaire abusif) ne pourraient lire aucun contenu car ils n'ont pas la passphrase utilisatrice. Cyora elle-même ne peut pas lire le contenu, pour la même raison.

Algorithmes utilisés

ÉtapeAlgorithmeParamètres
Dérivation de clé depuis passphraseArgon2idmemory 64 MiB, 3 itérations, 1 lane, hash 256 bits
Chiffrement symétriqueAES-256 GCMnonce 12 bytes, tag MAC 16 bytes
Génération de sel et nonceRandom.secure()16 bytes sel, 12 bytes nonce, par blob
Authentification serveurSupabase Auth (JWT)email + mot de passe, expiration 1h refreshable
Permissions stockagePostgres Row Level Securityauth.uid()::text = (storage.foldername(name))[1]

Argon2id a été choisi parce que c'est l'algorithme officiel de hashing de mots de passe, gagnant du Password Hashing Competition. Résistant aux attaques par GPU et ASIC. Les paramètres (64 MiB, 3 itérations) sont dimensionnés pour ~1-2 secondes de dérivation sur mobile, suffisamment lent pour rendre le brute-force coûteux mais acceptable pour l'UX.

AES-256-GCM est le standard NIST pour le chiffrement authentifié. Le mode GCM intègre un MAC : toute altération du blob (même un bit flip) provoque un échec de déchiffrement, pas une corruption silencieuse.

Format du blob chiffré

┌──────────┬─────────────┬─────────────┬─────────────────────────┐
│ version  │    sel      │    nonce    │   ciphertext + MAC      │
│  1 byte  │  16 bytes   │  12 bytes   │       variable          │
└──────────┴─────────────┴─────────────┴─────────────────────────┘

La version permet de migrer vers de nouveaux algorithmes plus tard sans casser la compatibilité avec les anciens blobs (ex. passage à Argon2id v1.4 ou ChaCha20-Poly1305).

Sel et nonce sont stockés en clair en préfixe du blob — c'est sans risque, ils sont publics par construction (un sel n'a pas besoin d'être secret, juste unique).

Pipeline upload (depuis ton appareil vers le serveur)

  1. L'app sérialise le journal corporel local en JSON UTF-8.
  2. EncryptionCodec.encrypt(passphrase, plaintext) :
    • Génère un sel aléatoire de 16 bytes.
    • Dérive une clé de 256 bits via Argon2id depuis (passphrase + sel).
    • Génère un nonce aléatoire de 12 bytes.
    • Chiffre le plaintext en AES-256-GCM.
    • Concatène [v1][salt][nonce][cipher+mac].
  3. Le blob est uploadé sur journal-blobs/<ton_user_id>/snapshot.bin.
  4. La RLS Supabase rejette tout autre user qui tenterait de lire ce blob.

Pipeline download (depuis un autre appareil)

  1. L'app télécharge le blob (RLS valide la permission via le JWT du caller).
  2. EncryptionCodec.decrypt(passphrase, blob) :
    • Lit version, sel, nonce dans le préfixe.
    • Dérive la clé via Argon2id depuis (passphrase + sel) — donne la même clé qu'à l'upload si la passphrase est correcte.
    • Tente le déchiffrement AES-GCM.
    • Si MAC valide → plaintext exposé. Si invalide → exception, aucune donnée déchiffrée.
  3. Le plaintext JSON est re-désérialisé et hydraté dans la base SQLite locale.

Données stockées localement (sans sync)

Si tu n'actives pas la sync, tes données ne quittent jamais ton appareil. La base SQLite est dans le sandbox app Android (/data/data/com.cyora.cyora/databases/), accessible uniquement par l'app elle-même, et chiffrée par le système si ton téléphone a un mot de passe d'écran de verrouillage (vrai sur quasi tous les Android 6+).

Cyora désactive aussi explicitement la sauvegarde automatique vers Google Drive via android:allowBackup="false" et dataExtractionRules. Donc les données ne fuitent pas dans ton compte Google.

Sous-traitants

Cyora utilise les services suivants uniquement si tu actives les fonctions correspondantes :

ServicePaysRôleDonnées reçues
SupabaseRégion UE StockholmAuth + stockage blobs chiffrésEmail + mot de passe Supabase, blobs opaques
StripeIrlande / USAPaiement PremiumEmail, moyen de paiement, statut abonnement
CloudflareMondialHébergement de cyora.appLogs HTTP minimaux

Aucun de ces services ne reçoit le contenu de ton journal en clair.

Limites assumées

Pour qu'il n'y ait pas de surprise, voici ce que Cyora ne peut pas protéger contre.

Perte de la passphrase

Si tu oublies ta passphrase, tes données synchronisées sont perdues définitivement. C'est l'invariant cryptographique : on n'a pas de mécanisme de récupération côté serveur, sinon on aurait l'accès en clair. Note ta passphrase quelque part de sûr (gestionnaire de mots de passe, papier dans un coffre).

Compromis physique de l'appareil

Si un attaquant a accès physique à ton téléphone déverrouillé, il peut lire la base SQLite locale. Aucune app ne peut empêcher ça sans hardware-backed encryption qui n'est pas disponible uniformément sur tous les Android.

Brute-force d'une passphrase faible

Argon2id rend l'attaque coûteuse (~1-2 secondes par tentative sur du matériel haute performance), mais une passphrase « 1234 » reste vulnérable à un attaquant motivé. Choisis une passphrase de ≥ 12 caractères, idéalement une phrase mémorable (ex. « le-ciel-est-bleu-12-fois »).

Attaque sur l'app elle-même

Si un attaquant parvient à modifier le binaire de l'app installée sur ton téléphone (par root + injection), il peut intercepter ta passphrase au moment où tu la tapes. C'est un scénario qui dépasse Cyora — pratiquement aucune app ne peut s'en protéger.

Comment vérifier toi-même

Le code source est public sur github.com/jpbenoit/cyora sous licence AGPL-3.0. Fichiers à auditer :

Tu peux aussi builder l'app toi-même à partir des sources et la comparer avec celle distribuée sur le Play Store (build reproductible).

Contact sécurité

Si tu trouves une vulnérabilité, contacte security@cyora.app (ou support@cyora.app). On ne propose pas de bug bounty financière au lancement, mais on accuse réception en 48 h et on fixe les bugs critiques rapidement. Tout chercheur sécurité responsable est crédité dans nos release notes (avec son consentement).