Comment sécuriser les jetons stockés sur mobile
- Accueil
- Architecture Cyber
- Comment sécuriser les jetons stockés sur mobile
Comment sécuriser les jetons stockés sur mobile
Section intitulée « Comment sécuriser les jetons stockés sur mobile »Un jeton d’authentification ou un cache métier stocké dans une application mobile reste exploitable dès que l’application peut le relire. Le chiffrement de la partition protège surtout un appareil éteint, pas une session ouverte, un appareil déverrouillé sous contrainte ou un environnement compromis. La recommandation est nette : conserver les secrets de session dans Keychain iOS ou Android Keystore, avec une politique d’accès alignée sur la durée et la sensibilité du secret.
Un téléphone verrouillé ressemble à une maison fermée quand personne n’est là. Mais si quelqu’un obtient la clé ou entre pendant qu’une porte est ouverte, les papiers posés sur la table restent accessibles. Les objets précieux doivent donc être rangés dans un meuble qui demande une autorisation distincte.
→ Lire l’explication complète de la marmotte
Chiffrement de partition et exposition des données locales
Section intitulée « Chiffrement de partition et exposition des données locales »Le chiffrement de stockage du système protège les blocs du disque lorsque l’appareil est éteint ou avant le premier déverrouillage. Il limite l’extraction physique directe de la mémoire persistante, notamment après retrait de la puce de stockage. C’est nécessaire. Ce n’est pas une frontière applicative.
Sur iOS, la protection des fichiers dépend de classes de protection liées à l’état de verrouillage. Sur Android, le chiffrement basé sur fichier sépare les données accessibles après le premier déverrouillage de celles qui restent indisponibles avant celui-ci. Dans les deux cas, dès que l’utilisateur déverrouille l’appareil et que le système autorise l’application à lire son répertoire privé, un fichier de préférences, une base SQLite ou un cache chiffré avec une clé embarquée redevient lisible.
Un jeton dans SharedPreferences, NSUserDefaults, un fichier JSON ou une base locale n’est donc pas protégé par le seul fait que le système chiffre le disque. L’application détient le droit de le lire, son processus aussi. Un outil d’instrumentation attaché à ce processus peut intercepter la valeur avant son écriture, après sa lecture ou directement dans la mémoire. Chiffrer un fichier avec une clé codée dans l’application déplace seulement le secret de quelques lignes. Le voleur n’a pas besoin de magie, juste d’un désassembleur et de café.
Le compromis central est le suivant : exiger une authentification locale ou un appareil récemment déverrouillé réduit l’exposition lors d’un vol, mais dégrade les usages en arrière-plan, la synchronisation silencieuse et la reprise automatique de session. Pour un jeton de rafraîchissement permettant d’émettre des jetons d’accès, ce coût est justifié. Pour un cache non sensible et reconstituable, il ne l’est généralement pas.
La règle d’architecture est de classer les données avant de choisir un magasin :
- un jeton d’accès court, idéalement quelques minutes, doit rester en mémoire autant que possible ;
- un jeton de rafraîchissement doit être stocké dans le magasin sécurisé natif ;
- un cache contenant des données personnelles, des documents ou des identifiants métier doit être chiffré par une clé non exportable ;
- une donnée reconstituable depuis le serveur ne mérite pas automatiquement une persistance locale ;
- un secret de longue durée dans le paquet applicatif est un secret déjà divulgué.
Cette classification rejoint la séparation entre secrets applicatifs, identités et clés décrite dans la gestion des clés et des secrets. Un stockage local sécurisé ne remplace ni une durée de vie courte, ni une révocation côté serveur. Il protège une copie locale, pas l’autorité qui accepte le jeton.
Keychain iOS et classes d’accessibilité
Section intitulée « Keychain iOS et classes d’accessibilité »Le Keychain iOS est un magasin du système destiné aux secrets : mots de passe, clés privées, jetons de session et données d’identité. Sa valeur vient moins du chiffrement que du contrôle d’accès associé à chaque entrée. Une application peut demander qu’un élément soit lisible uniquement lorsque l’appareil est déverrouillé, après authentification locale, ou seulement sur l’appareil où il a été créé.
Pour un jeton de rafraîchissement, la politique recommandée est kSecAttrAccessibleWhenUnlockedThisDeviceOnly. L’élément reste inaccessible quand l’appareil est verrouillé et ne migre pas vers un autre appareil via une sauvegarde ou une restauration. Cette dernière propriété est cruciale : restaurer un jeton vivant sur un terminal différent contourne le lien entre session et appareil.
kSecAttrAccessibleAfterFirstUnlock vise les tâches de fond après un premier déverrouillage. Cette classe facilite les notifications silencieuses et les synchronisations, mais augmente la fenêtre d’exposition : après le premier déverrouillage suivant un redémarrage, la donnée peut être accessible sans nouvel acte utilisateur. Elle ne convient pas à un jeton permettant une élévation de privilèges ou l’accès à des données particulièrement sensibles.
kSecAccessControlUserPresence ou une politique biométrique peut imposer une validation locale à chaque lecture. Cette protection est pertinente pour une signature de paiement, une clé de déchiffrement métier ou une opération irréversible. L’imposer sur un jeton nécessaire à chaque appel réseau transforme rapidement l’application en portique de métro. Le bon découpage consiste à protéger la capacité de renouveler une session, pas chaque requête banale.
Les groupes d’accès Keychain demandent une attention particulière. Deux applications partageant un groupe peuvent lire les mêmes éléments si leurs droits de signature l’autorisent. Ajouter une extension, un partage ou une application compagnon élargit donc la surface d’attaque. Un jeton destiné à l’application principale ne doit pas rejoindre un groupe partagé par confort de développement.
Android Keystore, clés non exportables et authentification locale
Section intitulée « Android Keystore, clés non exportables et authentification locale »Android Keystore permet de générer ou d’importer des clés utilisables par l’application sans rendre leur matière exportable. Selon l’appareil et la clé, l’opération cryptographique s’exécute dans un environnement d’exécution de confiance, ou Trusted Execution Environment (TEE), voire dans un composant matériel dédié. Cette distinction doit être vérifiée par l’attestation de clé lorsque le niveau matériel conditionne une décision de sécurité.
Le Keystore ne stocke pas le jeton à la place de l’application. Il conserve une clé. L’application chiffre le jeton avec cette clé, stocke le texte chiffré, le vecteur d’initialisation et les métadonnées dans son espace privé, puis demande au Keystore de déchiffrer. La clé reste non exportable. C’est précisément ce qui manque à un chiffrement applicatif dont la clé vit dans un fichier, une constante ou une bibliothèque native. Changer Java par Rust ou cacher une clé dans du code C ne crée pas un coffre. Cela crée une chasse au trésor pour stagiaires motivés.
Pour un chiffrement symétrique, utiliser AES (Advanced Encryption Standard) en mode GCM (Galois Counter Mode). GCM assure confidentialité et intégrité, à condition de ne jamais réutiliser le même vecteur d’initialisation avec la même clé. Le vecteur doit être généré par l’implémentation cryptographique et conservé avec le texte chiffré. Ajouter des données authentifiées, par exemple l’identifiant de compte et la version du format, évite de déplacer un chiffré entre deux contextes.
L’option setUserAuthenticationRequired(true) lie l’usage de la clé à une authentification locale. Sur les versions récentes, une fenêtre maximale peut être configurée. Une fenêtre courte réduit l’exposition lors d’un appareil déverrouillé laissé sans surveillance. Elle interdit aussi les renouvellements de session en arrière-plan au-delà de cette durée. Il faut choisir selon le scénario métier, pas selon la démonstration vendeur du mois.
Un appareil enraciné, un débogage activé sur un terminal compromis, ou une application exécutée dans le même contexte utilisateur restent des risques à traiter. Le Keystore réduit l’extraction de clé, mais ne protège pas une opération de déchiffrement légitime déclenchée dans un processus contrôlé. L’attestation, Play Integrity et la détection de compromission peuvent alimenter une décision serveur, jamais servir de preuve absolue d’intégrité.
Exemple Android : clé liée au déverrouillage
Section intitulée « Exemple Android : clé liée au déverrouillage »Cet exemple requiert Android API (Application Programming Interface) 30 ou supérieure pour setUserAuthenticationParameters. Il crée une clé AES utilisable uniquement après une authentification biométrique forte ou le code de déverrouillage de l’appareil. Le texte chiffré, son vecteur d’initialisation et les données authentifiées doivent être stockés ensemble dans l’espace privé de l’application.
// API 30+ : crée une clé non exportable dans Android Keystore.val spec = KeyGenParameterSpec.Builder( "refresh-token-v1", KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(true) // La clé reste utilisable 30 secondes après une authentification locale. .setUserAuthenticationParameters( 30, KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL ) // Un nouvel enrôlement biométrique invalide la clé. .setInvalidatedByBiometricEnrollment(true) .build()
val generator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")generator.init(spec)val key = generator.generateKey()
// À chaque chiffrement, conserver cipher.iv avec le texte chiffré.// Ne jamais réutiliser cet IV avec cette clé.L’invalidation après ajout ou retrait d’une donnée biométrique impose une stratégie de reprise. À l’échec de déchiffrement, supprimer le jeton local, invalider la session côté serveur si possible, puis demander une authentification complète. Ne jamais remplacer silencieusement l’erreur par une clé logicielle de secours. Une dégradation qui conserve le confort tout en supprimant la frontière de sécurité est un contournement, pas une disponibilité.
Cycle de vie, révocation et supervision des secrets mobiles
Section intitulée « Cycle de vie, révocation et supervision des secrets mobiles »Le stockage sécurisé ne garantit pas qu’un jeton déjà extrait avant sa révocation cesse d’être accepté. La révocation doit être vérifiée côté serveur lors du renouvellement, et les jetons d’accès doivent rester courts. Un JSON Web Token (JWT) autoportant avec une durée de plusieurs jours rend la révocation lente ou coûteuse, même s’il dort dans le meilleur Keychain du monde.
Lier le jeton de rafraîchissement à une installation, à une clé publique d’appareil ou à une preuve cryptographique réduit le rejeu depuis un autre terminal. Cette liaison exige un protocole serveur : émission, renouvellement, remplacement atomique, détection de réutilisation et invalidation de famille de jetons. Le client seul ne peut pas résoudre ce problème. Les sujets de fédération et de durée de session relèvent aussi de l’architecture des identités.
Prévoir les événements suivants dès la conception :
- désinstallation et réinstallation, qui doivent conduire à une nouvelle session ;
- restauration de sauvegarde, qui ne doit pas réanimer un secret lié à un autre appareil ;
- changement de code de verrouillage ou de biométrie, pouvant invalider une clé ;
- perte de synchronisation de l’horloge, qui ne doit pas prolonger arbitrairement une session ;
- réinitialisation d’usine, qui détruit les clés locales et impose une réauthentification.
La supervision doit remonter des événements de sécurité sans journaliser de secret : échecs de déchiffrement, invalidations de clé, renouvellements refusés, réutilisation de jeton et changements d’empreinte d’appareil. Un identifiant de session pseudonymisé suffit pour corréler les événements. Mettre le jeton brut dans les journaux transforme une investigation en distribution gratuite de clés de chambre.
Les sauvegardes applicatives doivent exclure les données chiffrées dont la clé est liée au matériel, sauf si une restauration contrôlée est explicitement prévue. Dans ce cas, chiffrer la donnée pour une nouvelle clé créée sur l’appareil restauré après réauthentification. Le modèle local-first peut persister davantage de données, mais il doit distinguer la disponibilité hors ligne de la conservation irrévocable de secrets. Cette frontière est détaillée dans l’architecture local-first.
🐝 Ce qui arrive si on s’en passe
Section intitulée « 🐝 Ce qui arrive si on s’en passe »Sans magasin sécurisé natif, le jeton de rafraîchissement suit le sort des fichiers ordinaires de l’application : une sauvegarde locale non chiffrée sur ordinateur, ou une copie logique du bac à sable réalisée sur un appareil déverrouillé, l’emporte en clair, lisible ensuite hors du téléphone, sans instrumentation ni accès prolongé. La copie ne laisse aucune trace locale : sans clé non exportable, aucun échec de déchiffrement ni invalidation de clé ne se produit, donc la supervision ne reçoit aucun signal. Côté serveur, le jeton copié se présente comme légitime ; seule la détection de réutilisation lors d’une rotation peut révéler l’usage concurrent, et uniquement si le détenteur légitime renouvelle sa session avant que le jeton copié n’expire.
🦫 Explication de la marmotte 🍫
Section intitulée « 🦫 Explication de la marmotte 🍫 »Le téléphone chiffre son espace de stockage lorsqu’il est éteint ou verrouillé. Cela empêche surtout de lire directement son contenu en retirant la mémoire ou en copiant les fichiers. Après déverrouillage, le système autorise les applications à relire leurs données. Si une application garde un jeton de connexion dans un fichier ordinaire, une personne qui contrôle le téléphone déverrouillé ou l’application peut récupérer ce jeton.
Les systèmes mobiles proposent donc un emplacement spécial pour les secrets. Sur iPhone, il peut refuser de fournir une donnée tant que le téléphone est verrouillé. Sur Android, il peut conserver une clé de chiffrement que l’application ne peut pas copier. L’application chiffre alors le jeton avec cette clé et conserve seulement le résultat chiffré dans son stockage habituel.
Certaines clés demandent aussi une validation par code ou biométrie avant chaque usage, ou pendant une courte période. Si cette validation échoue, si une donnée biométrique change ou si la clé est supprimée, le jeton devient inutilisable. L’application doit alors supprimer sa copie locale et demander une nouvelle connexion.
Cette protection ne suffit pas si le serveur accepte le même jeton pendant des mois. Le serveur doit limiter sa durée, pouvoir le refuser et détecter sa réutilisation. Les journaux doivent signaler les échecs sans jamais enregistrer le jeton lui-même.
À lire aussi
- Politique de sécurité des extensions navigateur en entreprisePourquoi une extension légitime peut devenir malveillante après une mise à jour ou un rachat, et comment gérer les extensions de navigateur autorisées en entreprise.
- Sécuriser les données en mémoire dans le cloudMettre en œuvre le confidential computing pour protéger les données pendant le traitement des workloads cloud.
- Sécuriser les données d’élèves mineurs en EdTechArchitecture de sécurité des environnements EdTech traitant des données d’élèves mineurs, avec contraintes réglementaires, identité, chiffrement et exploitation.
- Sécuriser les satellites et les liaisons montantesSécurité spatiale des satellites, des liaisons montantes et de leur surface d'attaque orbitale.
Pages voisines
La citent
- Pièges OAuth 2.1 : PKCE, redirections et scopes
- SSO, SCIM et journaux d'audit pour un assistant IA d'entreprise
Comment sécuriser les jetons stockés sur mobile
Elle cite
