# 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