# Sécurité — iMedia TV+ ## Principes 1. **Aucun secret en clair sur le disque ou dans les logs** 2. **Chiffrement de bout en bout** du stockage local des credentials 3. **Isolation** : l'application ne partage pas les credentials avec d'autres apps 4. **Principle of least privilege** : permissions Android minimales ## Stockage des credentials ### Implémentation ```kotlin // data/storage/SecureStorage.kt import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey class SecureStorage(context: Context) { private val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() private val prefs = EncryptedSharedPreferences.create( context, "imedia_secure_prefs", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) fun saveCredentials(server: String, username: String, password: String) { prefs.edit() .putString("server_url", server) .putString("username", username) .putString("password", password) .apply() } fun getCredentials(): Credentials? { val server = prefs.getString("server_url", null) val username = prefs.getString("username", null) val password = prefs.getString("password", null) return if (server != null && username != null && password != null) { Credentials(server, username, password) } else null } fun clearCredentials() { prefs.edit().clear().apply() } } ``` Pour les favoris et la chaîne épinglée (données non sensibles), la version 1 utilisait des `SharedPreferences` classiques — acceptable. Si on veut tout unifier, on utilise `EncryptedSharedPreferences` partout. ### Ce qui est chiffré | Donnée | Niveau | Sensibilité | |--------|--------|-------------| | Server URL | AES-256 GCM | Haute | | Username | AES-256 GCM | Haute | | Password | AES-256 GCM | Critique | | Favoris | AES-256 SIV keys | Moyenne | | Chaîne épinglée | AES-256 SIV keys | Basse | ## Fichier de mapping des endpoints **Important :** les URLs Xtream construites avec le builder ne doivent pas non plus être logguées. ```kotlin // util/XtreamUrlBuilder.kt class XtreamUrlBuilder(private val credentials: Credentials) { fun liveUrl(streamId: Int): String = "${credentials.server}/live/${credentials.username}/${credentials.password}/$streamId.m3u8" // ⚠️ Attention : cette méthode est appelée par PlayerScreen // — NE PAS logger son résultat } ``` ## Règles strictes ### ✅ À faire - Utiliser `Timber.d` pour les logs de debug (supprimés en release par ProGuard) - Vérifier les inputs utilisateur avant de les envoyer (URL bien formée) - Timeout réseau configuré (15s connect, 30s read) - Nettoyer les credentials à la déconnexion - Minify + ProGuard en release (obfuscation) ### ❌ Interdit ```kotlin // JAMAIS : Log.d("Auth", "Login with $username / $password") Timber.d("Server URL: $serverUrl") println("Password is: $password") Log.e("API", "Stream URL: ${urlBuilder.liveUrl(42)}") // En release, ces logs ne devraient pas survivre, // mais ne prenez pas le risque en debug non plus. ``` ## ProGuard ```pro # app/proguard-rules.pro # Sécurité : ne pas exposer les données chiffrées -keepclassmembers class ma.tkgraph.xtreamtv.data.storage.** { *; } # OkHttp / Retrofit déjà couverts par leurs propres rules # Ne pas garder de logs en release -assumenosideeffects class timber.log.Timber { public static *** d(...); public static *** v(...); } ``` ## Permissions Android ```xml ``` L'application ne lit pas de fichiers locaux et ne sauvegarde que des données dans son propre espace de stockage privé — pas besoin de permissions de stockage. ## HTTPS Bien que le protocole Xtream utilise souvent HTTP (non chiffré), l'application doit : 1. **Accepter HTTP** pour la compatibilité 2. **Préférer HTTPS** si le serveur le supporte 3. **Ne pas désactiver la validation** SSL/TLS (pas de `TrustAllCertificates`) Si le fournisseur Xtream le permet, les credentials transitent en HTTPS, ce qui protège le mot de passe en transit. ## Audit Une amélioration future possible : `Sentry` ou `Firebase Crashlytics` pour monitorer les crashs. Attention cependant à ne pas envoyer de données sensibles dans les rapports : ```kotlin // Dans Application.onCreate() FirebaseCrashlytics.getInstance().setCustomKey("screen", "login") // ❌ PAS FirebaseCrashlytics.getInstance().setCustomKey("username", username) ``` ## Checklist de sécurité avant release - [ ] Aucun log de credentials dans le code (grep "password" dans les fichiers source) - [ ] EncryptedSharedPreferences en place, pas de fallback SharedPreferences - [ ] Timeouts réseau configurés - [ ] Permissions minimales (seulement INTERNET + ACCESS_NETWORK_STATE) - [ ] ProGuard activé en release - [ ] Pas de validation SSL désactivée - [ ] Gestion de la déconnexion efface TOUTES les données