Garden of KnowledgeApplied Sciences › Computer Science › Software › Security › Platform Security
March 22, 2026

Sécurité Mobile — iOS et Android

La sécurité mobile couvre la protection des appareils, systèmes d’exploitation et applications mobiles. Avec plus de 6 milliards de smartphones dans le monde, la surface d’attaque est considérable.

Modèles de sécurité comparés§

graph LR
    subgraph Android
        a_kernel[Linux Kernel]
        a_kernel --> a_hal[HAL]
        a_hal --> a_art[ART Runtime]
        a_art --> a_app[Application]
        a_sandbox[Sandbox par UID\nunique par app]
        a_perm[Système de permissions\nruntime / install-time]
    end

    subgraph iOS
        i_kernel[XNU Kernel / Darwin]
        i_kernel --> i_sec[Secure Enclave]
        i_kernel --> i_sand[Sandbox Seatbelt\n(profils par app)]
        i_sand --> i_app[Application]
        i_sign[Code Signing\nobligatoire]
        i_boot[Secure Boot Chain]
    end
AspectAndroidiOS
NoyauLinuxXNU (BSD/Mach)
SourcesOpen Source (AOSP)Fermé
DistributionGoogle Play + sideloadingApp Store uniquement
SandboxUID Unix par appSeatbelt profiles
PermissionsDynamiques (runtime)Dynamiques (runtime)
ChiffrementPlein disque / par fichierData Protection (4 classes)
Jailbreak/RootPlus fréquentPlus difficile

OWASP Mobile Top 10§

#VulnérabilitéDescription
M1Improper Credential UsageCredentials codés en dur, mal stockés
M2Inadequate Supply Chain SecuritySDK tiers malveillants, dépendances compromises
M3Insecure Authentication/AuthorizationAuthentification faible, bypass autorisation
M4Insufficient Input/Output ValidationXSS, injection via données externes
M5Insecure CommunicationTLS absent, certificate pinning manquant
M6Inadequate Privacy ControlsCollecte de données excessive, logs sensibles
M7Insufficient Binary ProtectionsPas d’obfuscation, débogage actif
M8Security MisconfigurationBackup autorisé, debug mode en production
M9Insecure Data StorageSharedPreferences non chiffrées, SQLite en clair
M10Insufficient CryptographyAlgorithmes faibles, mauvaise gestion des clés

Stockage insécurisé — Android§

// MAUVAIS — SharedPreferences non chiffrées
SharedPreferences prefs = getSharedPreferences("auth", MODE_PRIVATE);
prefs.edit().putString("token", accessToken).apply();
// Lisible dans /data/data/com.app/shared_prefs/ avec root

// MAUVAIS — Base SQLite non chiffrée
// Lisible dans /data/data/com.app/databases/

// BIEN — EncryptedSharedPreferences (Jetpack Security)
MasterKey masterKey = new MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build();
SharedPreferences encryptedPrefs = EncryptedSharedPreferences.create(
    context, "auth", masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// BIEN — Android Keystore (clés non exportables, hardware-backed)
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");

Stockage insécurisé — iOS§

// MAUVAIS — UserDefaults en clair
UserDefaults.standard.set(token, forKey: "access_token")
// Stocké dans .plist lisible sans protection

// MAUVAIS — Documents/ directory (sauvegardé sur iCloud par défaut)

// BIEN — Keychain (chiffré, hardware-backed sur Secure Enclave)
let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: "access_token",
    kSecValueData as String: tokenData,
    kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)

// BIEN — Data Protection class (chiffrement par état de l'appareil)
// NSFileProtectionCompleteUnlessOpen = chiffré sauf si fichier ouvert

Communications non sécurisées§

Certificate Pinning§

Vérifie que le certificat serveur correspond à un certificat connu (défense contre MITM même avec un CA compromis).

// Android — OkHttp Certificate Pinning
OkHttpClient client = new OkHttpClient.Builder()
    .certificatePinner(new CertificatePinner.Builder()
        .add("api.example.com",
             "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
        .build())
    .build();
// iOS — URLSession avec pinning manuel
func urlSession(_ session: URLSession,
                didReceive challenge: URLAuthenticationChallenge,
                completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    guard let serverTrust = challenge.protectionSpace.serverTrust,
          let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }
    // Comparer avec le certificat embarqué
}

Contournement du pinning (test) : Frida, objection, SSL Kill Switch 2, Proxyman.

Jailbreak et Root§

Détection du jailbreak (iOS)§

func isJailbroken() -> Bool {
    let paths = ["/Applications/Cydia.app", "/usr/sbin/sshd",
                 "/bin/bash", "/etc/apt", "/private/var/lib/apt/"]
    for path in paths {
        if FileManager.default.fileExists(atPath: path) { return true }
    }
    // Test d'écriture hors sandbox
    do {
        try "test".write(toFile: "/private/test.txt",
                         atomically: true, encoding: .utf8)
        return true
    } catch { return false }
}

Contournement des détections : Liberty Lite, A-Bypass, Shadow — les détections simples sont facilement contournables.

Détection du root (Android)§

boolean isRooted() {
    String[] paths = {"/system/app/Superuser.apk", "/sbin/su",
                      "/system/bin/su", "/system/xbin/su"};
    for (String path : paths) {
        if (new File(path).exists()) return true;
    }
    try {
        Process p = Runtime.getRuntime().exec("su");
        return true;
    } catch (IOException e) { return false; }
}

SafetyNet / Play Integrity API : solution Google plus robuste pour attester l’intégrité de l’appareil.

Analyse d’applications mobiles§

Android — APK§

# Décompiler APK
apktool d application.apk -o decompiled/

# Décompiler DEX → Java (lisible)
jadx-gui application.apk

# Chercher des secrets codés en dur
grep -r "password\|secret\|api_key\|token" decompiled/ --include="*.xml" --include="*.smali"

# Analyser avec MobSF (framework automatique)
docker run -it --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf

# Analyser les permissions dans AndroidManifest.xml
cat decompiled/AndroidManifest.xml | grep "uses-permission"

iOS — IPA§

# Extraire l'IPA
unzip app.ipa -d extracted/

# Analyser le binaire Mach-O
otool -L extracted/Payload/App.app/App    # Bibliothèques liées
nm -u extracted/Payload/App.app/App       # Symboles non résolus
strings extracted/Payload/App.app/App | grep -i "key\|token\|pass"

# Décompiler avec Ghidra ou Hopper Disassembler
# Analyser avec MobSF

Analyse dynamique§

# Frida — instrumentation dynamique (Android/iOS)
frida-ps -U                               # Lister les processus
frida -U -l script.js com.example.app    # Injecter un script

# Script Frida — hooker une méthode
Java.perform(function() {
    var MainActivity = Java.use('com.example.MainActivity');
    MainActivity.checkPassword.implementation = function(pass) {
        console.log('Password: ' + pass);
        return true;  // Bypass
    };
});

# objection — interface Frida simplifiée
objection -g com.example.app explore
# ios keychain dump
# android sslpinning disable

Vecteurs d’attaque courants§

Intent Hijacking (Android)§

Les Intents implicites peuvent être interceptés par une application malveillante.

<!-- Vulnérable : exported=true sans restriction -->
<activity android:name=".SecretActivity" android:exported="true"/>

<!-- Sécurisé : permission personnalisée -->
<activity android:name=".SecretActivity"
          android:exported="true"
          android:permission="com.example.CUSTOM_PERMISSION"/>
myapp://reset-password?token=abc123

Si le token de reset est passé dans l’URL, un attaquant peut lancer ce deep link depuis une autre app.

WebView XSS§

// MAUVAIS
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new MyBridge(), "Android");
// Un XSS dans la WebView peut appeler les méthodes Java exposées

// BIEN — restreindre les origines
webView.getSettings().setAllowFileAccess(false);
webView.getSettings().setAllowContentAccess(false);

Bonnes pratiques de développement sécurisé§

DomainePratique
StockageKeychain (iOS) / Keystore + EncryptedSharedPreferences (Android)
RéseauTLS 1.2+, certificate pinning, HSTS
AuthentificationBiométrie + Keychain/Keystore, expiration des sessions
CodeObfuscation (ProGuard/R8 Android, LLVM obfuscation iOS), pas de logs en prod
PermissionsPrincipe du moindre privilège, demander au moment de l’usage
SauvegardesExclure les données sensibles des sauvegardes cloud
IntégritéDétection root/jailbreak, Play Integrity API, SafetyNet
SecretsVariables d’environnement CI, jamais dans le code source
—The Gardener