Files
iptv-android-tv-hermes/docs/SECURITY.md
T

5.3 KiB

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

// 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.

// 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

// 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

# 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

<!-- AndroidManifest.xml — le strict minimum -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- ⚠️ PAS de READ_EXTERNAL_STORAGE ni WRITE_EXTERNAL_STORAGE -->

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 :

// 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