PD-262 — iOS Anti-tampering : detection jailbreak et protection Frida¶
1. Contexte¶
L'application iOS ProbatioVault stocke des cles cryptographiques sensibles (Kmaster, enveloppes de recovery, tokens SRP). La review securite PD-174 a identifie deux vulnerabilites critiques :
- S-01 : bypass de l'authentification biometrique via hook JavaScript (Frida/Objection)
- S-02 : instrumentation dynamique permettant l'extraction de secrets en memoire
Le mecanisme BIOMETRY_CURRENT_SET (Keychain) protege contre le changement d'empreintes mais ne couvre pas le hooking JS. PD-262 implemente les contre-mesures anti-tampering planifiees dans le REX PD-174.
2. Objectif¶
Implementer un module natif iOS de detection anti-tampering qui :
- Detecte les environnements compromis (jailbreak, instrumentation dynamique, signature invalide)
- Verrouille l'application immediatement en cas de detection (fail-closed)
- Purge les secrets et caches sensibles en memoire et sur disque
- Journalise les causes de detection pour audit et analyse securite
- Reporte les evenements au backend en best effort
3. Decisions PO¶
3.1 Frequence des controles¶
Les controles anti-tampering sont executes : - Au cold start : check bloquant avant deverrouillage des secrets applicatifs - Au retour foreground : re-check a chaque transition background → foreground - Timer periodique : toutes les 30-60 secondes pendant la session ouverte
Justification : un check uniquement au lancement laisse un trou si Frida/Objection est attache apres demarrage.
3.2 Granularite du verrouillage¶
- Lockout unique : un seul etat fonctionnel
tampered = true→ hard lockout - Causes distinguees en audit :
JAILBREAK_DETECTED,FRIDA_DETECTED,CODESIGN_INVALID,DYLIB_INJECTION,SANDBOX_VIOLATION
Justification : UX et logique metier simples, securite homogene, meilleure telemetrie.
3.3 Effacement en cas de detection¶
Purge obligatoire immediate : - Cles derivees en memoire - Tokens session / refresh tokens - Caches crypto - Fichiers temporaires dechiffres - Artefacts de preview
Donnees persistees locales : - Conserver les blobs deja chiffres (inexploitables sans secrets) - Marquer le store local comme inaccessible tant que l'app est lockee - Pas de wipe complet automatique (destructif, difficile a justifier UX/support)
3.4 Reporting backend¶
- Lockout et purge declenches localement et immediatement (pas de dependance reseau)
- Tentative best effort d'envoi d'un evenement d'audit
- Payload minimal :
event_type,reason_code,app_version,build_number,device_idpseudonymise,timestamp,environment - Aucune donnee sensible, pas de dump memoire
3.5 Mode release uniquement¶
- Desactive en DEBUG et simulateur
- Active en Release / TestFlight / production
- Activable en QA via feature flag dedie sur device reel
Justification : ne pas bloquer le dev, les tests RN, le debug natif, l'instrumentation legitime de QA.
3.6 Implementation 100% native¶
- Detection, decision, purge et lockout dans le natif (Swift/Obj-C)
- Exposition a React Native via TurboModule minimal (interrogation d'etat / signal)
- Aucun check critique ne depend d'une logique JS modifiable a chaud
Justification : coherent avec l'origine du besoin — eviter le bypass par instrumentation JS (PD-174 S-01/S-02).
4. Perimetre fonctionnel¶
4.1 Detection jailbreak (multi-methodes)¶
| Methode | Description |
|---|---|
| Paths suspects | Verification /Applications/Cydia.app, /usr/sbin/sshd, /usr/bin/ssh, /etc/apt, /var/lib/dpkg, Sileo, Zebra |
| Sandbox integrity | Tentative d'ecriture dans /private/ (normalement interdit en sandbox) |
| Symbolic links | Detection de liens symboliques suspects sur les paths systeme |
| Fork test | Appel fork() (interdit en sandbox iOS non-jailbreak) |
| Dyld check | Verification des dylibs chargees via _dyld_image_count() / _dyld_get_image_name() |
4.2 Detection Frida/Objection¶
| Methode | Description |
|---|---|
| Port scanning | Scan ports par defaut 27042, 27043 (connexion locale) |
| Dyld injection | Detection de FridaGadget.dylib, frida-agent dans les dylibs chargees |
| Signatures memoire | Pattern matching sur frida-agent, gum-js-loop, LIBFRIDA en memoire |
4.3 Validation code signing¶
| Methode | Description |
|---|---|
| Bundle signature | Verification de l'integrite de la signature du bundle au runtime |
| Embedded provisioning | Verification coherence du profil de provisioning |
4.4 Comportement post-detection¶
- Flag
tampered = truepositionne (irreversible pour la session) - Purge immediate des secrets (cf. §3.3)
- Affichage ecran de lockout (non dismissable)
- Emission evenement audit backend (best effort)
- App inutilisable jusqu'a reinstallation propre
5. Hors perimetre¶
- Detection root Android (projet iOS uniquement)
- Wipe complet automatique des donnees persistees
- Mecanisme de deblocage distant (remote unlock)
- Detection de proxy SSL (Charles, mitmproxy) — story separee si besoin
- Obfuscation du code Swift (complementaire mais hors scope)
6. Dependances¶
| Dependance | Story | Statut |
|---|---|---|
| Keychain Kmaster | PD-98 | DONE |
| Biometric auth | PD-107 | DONE |
| Auto-lock | PD-174 | DONE (vulnerabilites S-01/S-02 identifiees) |
7. Risques identifies¶
| Risque | Impact | Mitigation |
|---|---|---|
| Faux positifs en production | Lockout d'utilisateurs legitimes | Tests extensifs sur devices reels, feature flag QA |
| Contournement par nouvelle technique | Protection depassee | Architecture modulaire, methodes empilees (defense in depth) |
| Hermes limitations (PD-97) | Polyfills ou APIs indisponibles | Module 100% natif, pas de dependance JS |
| Impact performance du timer | Battery drain, CPU | Intervalle 30-60s, checks legers, pas de I/O reseau dans le check |
8. Learnings injectes¶
- PD-174 :
BIOMETRY_CURRENT_SETne protege pas contre hook JS → anti-tampering natif obligatoire - PD-97 : Hermes n'a pas de WebAssembly, polyfills a valider en prod → implementation Swift pure
- PD-39 : fail-closed obligatoire pour features securite critique → pas de mode degrade