Categoria OWASP: MASVS-STORAGE: archiviazione
Panoramica
La divulgazione di informazioni dei log è un tipo di vulnerabilità in cui le app stampano dati sensibili nel log del dispositivo. Se esposte a utenti malintenzionati, queste informazioni sensibili possono essere di valore immediato, ad esempio le credenziali di un utente o le informazioni che consentono l'identificazione personale (PII), oppure possono consentire ulteriori attacchi.
Questo problema può verificarsi in uno dei seguenti scenari:
- Log generati dall'app:
- I log consentono intenzionalmente l'accesso ad attori non autorizzati, ma contengono accidentalmente dati sensibili.
- I log includono intenzionalmente dati sensibili, ma sono accessibili accidentalmente a soggetti non autorizzati.
- Log di errori generici che a volte potrebbero stampare dati sensibili, a seconda del messaggio di errore attivato.
- Log generati esternamente:
- I componenti esterni sono responsabili della stampa dei log che includono dati sensibili.
Le istruzioni Log.*
di Android scrivono nel buffer di memoria comune logcat
. A partire da Android 4.1 (livello API 16), solo le app di sistema con privilegi possono essere autorizzate ad accedere alla lettura di logcat
, dichiarando l'autorizzazione READ_LOGS
. Tuttavia, Android supporta un insieme incredibilmente diversificato di dispositivi le cui applicazioni precaricate a volte dichiarano il privilegio READ_LOGS
. Di conseguenza, la registrazione diretta in logcat
è sconsigliata perché è più soggetta a perdite di dati.
Assicurati che tutti i log in logcat
siano sottoposti a sanificazione nelle versioni non di debug dell'applicazione. Rimuovi tutti i dati che potrebbero essere sensibili. Come ulteriore precauzione, utilizza strumenti come R8 per rimuovere tutti i livelli di log tranne avviso ed errore. Se hai bisogno di log più dettagliati, utilizza lo spazio di archiviazione interno e gestisci direttamente i tuoi log anziché utilizzare il log di sistema.
Impatto
La gravità della classe di vulnerabilità relativa alla divulgazione di informazioni dei log può variare a seconda del contesto e del tipo di dati sensibili.In generale, l'impatto di questa classe di vulnerabilità è la perdita della riservatezza di informazioni potenzialmente critiche come PII e credenziali
Mitigazioni
Generali
Come misura preventiva generale durante la progettazione e l'implementazione, definisci i confini di attendibilità in base al principio del privilegio minimo. Idealmente, i dati sensibili non dovrebbero superare o raggiungere nessuna delle aree attendibili. Ciò rafforza la separazione dei privilegi.
Non registrare dati sensibili. Se possibile, registra solo le costanti di compilazione. Puoi utilizzare lo strumento ErrorProne per l'annotazione delle costanti in fase di compilazione.
Evita i log che stampano istruzioni che potrebbero contenere informazioni impreviste, inclusi dati sensibili, a seconda dell'errore attivato. Per quanto possibile, i dati stampati nei log e nei log degli errori devono includere solo informazioni prevedibili.
Evita di eseguire il logging in logcat
. Questo perché la registrazione su logcat
potrebbe diventare un problema di privacy a causa delle app con l'autorizzazione READ_LOGS
. Inoltre, non è efficace perché non può attivare avvisi o essere sottoposto a query. Consigliamo alle applicazioni di configurare il backend logcat
solo per le build degli sviluppatori.
La maggior parte delle librerie di gestione dei log consente di definire i livelli di log, il che consente di registrare quantità diverse di informazioni tra i log di debug e di produzione. Modifica il livello di log in modo che sia diverso da "debug" al termine del test del prodotto.
Rimuovi il maggior numero possibile di livelli di log dalla produzione. Se non puoi evitare di conservare i log in produzione, rimuovi le variabili non costanti dalle istruzioni log. Potrebbero verificarsi i seguenti scenari:
- Puoi rimuovere tutti i log dalla produzione.
- Devi conservare i log di avviso ed errore in produzione.
In entrambi i casi, rimuovi i log automaticamente utilizzando librerie come R8. Qualsiasi tentativo di rimuovere i log manualmente è soggetto a errori. Nell'ambito dell'ottimizzazione del codice, R8 può essere impostato per rimuovere in sicurezza i livelli di log che vuoi conservare per il debug, ma rimuovere in produzione.
Se hai intenzione di accedere in produzione, prepara i flag che puoi utilizzare per arrestare la registrazione in modo condizionale in caso di incidente. Gli indicatori di risposta agli incidenti devono dare la priorità a: sicurezza del deployment, velocità e facilità di implementazione, completezza dell'oscuramento dei log, utilizzo della memoria e costi delle prestazioni per l'analisi di ogni messaggio di log.
Rimuovi i log da logcat dalle build di produzione utilizzando R8.
In Android Studio 3.4 o nel plug-in Android Gradle 3.4.0 e versioni successive, R8 è il compilatore predefinito per l'ottimizzazione e la riduzione del codice. Tuttavia, devi abilitare R8.
R8 ha sostituito ProGuard, ma il file delle regole nella cartella principale del progetto si chiama ancora proguard-rules.pro
.Lo snippet seguente mostra un file proguard-rules.pro
di esempio che rimuove tutti i log dalla produzione tranne avvisi ed errori:
-assumenosideeffects class android.util.Log {
private static final String TAG = "MyTAG";
public static boolean isLoggable(java.lang.String, int);
public static int v(TAG, "My log as verbose");
public static int d(TAG, "My log as debug");
public static int i(TAG, "My log as information");
}
Il seguente file proguard-rules.pro
di esempio rimuove tutti i log dalla produzione:
-assumenosideeffects class android.util.Log {
private static final String TAG = "MyTAG";
public static boolean isLoggable(java.lang.String, int);
public static int v(TAG, "My log as verbose");
public static int d(TAG, "My log as debug");
public static int i(TAG, "My log as information");
public static int w(TAG, "My log as warning");
public static int e(TAG, "My log as error");
}
Tieni presente che R8 offre funzionalità di riduzione delle app e di rimozione dei log. Se vuoi utilizzare R8 solo per la funzionalità di rimozione dei log, aggiungi quanto segue al file proguard-rules.pro
:
-dontwarn **
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!code/allocation/variable
-keep class **
-keepclassmembers class *{*;}
-keepattributes *
Elimina eventuali log in produzione contenenti dati sensibili
Per evitare fughe di dati sensibili, assicurati che tutti i log inviati a logcat
siano sottoposti a sanificazione nelle versioni non di debug dell'applicazione. Rimuovi tutti i dati che potrebbero essere sensibili.
Esempio:
Kotlin
data class Credential<T>(val data: String) {
/** Returns a redacted value to avoid accidental inclusion in logs. */
override fun toString() = "Credential XX"
}
fun checkNoMatches(list: List<Any>) {
if (!list.isEmpty()) {
Log.e(TAG, "Expected empty list, but was %s", list)
}
}
Java
public class Credential<T> {
private T t;
/** Returns a redacted value to avoid accidental inclusion in logs. */
public String toString(){
return "Credential XX";
}
}
private void checkNoMatches(List<E> list) {
if (!list.isEmpty()) {
Log.e(TAG, "Expected empty list, but was %s", list);
}
}
Oscurare i dati sensibili nei log
Se devi includere dati sensibili nei log, ti consigliamo di sottoporli a sanificazione prima di stamparli per rimuovere o offuscare i dati sensibili. A tale scopo, utilizza una delle seguenti tecniche:
- Tokenizzazione. Se i dati sensibili sono archiviati in una cassetta di sicurezza, ad esempio un sistema di gestione della crittografia da cui è possibile fare riferimento ai secret tramite token, registra il token anziché i dati sensibili.
- Mascheramento dei dati. Il mascheramento dei dati è un processo irreversibile unidirezionale. Crea una versione dei dati sensibili che sembra strutturalmente simile all'originale, ma nasconde le informazioni più sensibili contenute in un campo. Esempio: sostituisci il numero di carta di credito
1234-5678-9012-3456
conXXXX-XXXX-XXXX-1313
. Prima di rilasciare l'app in produzione, ti consigliamo di completare una procedura di revisione della sicurezza per esaminare attentamente l'utilizzo del mascheramento dei dati. Avviso:non utilizzare il mascheramento dei dati nei casi in cui anche la divulgazione di una sola parte dei dati sensibili può influire notevolmente sulla sicurezza, ad esempio durante la gestione delle password. - Oscuramento. L'oscuramento è simile al mascheramento, ma nasconde tutte le informazioni contenute in un campo. Esempio: sostituisci il numero di carta di credito
1234-5678-9012-3456
conXXXX-XXXX-XXXX-XXXX
. - Filtro. Implementa le stringhe di formato nella libreria di log che preferisci, se non esistono già, per facilitare la modifica dei valori non costanti nelle istruzioni di log.
La stampa dei log deve essere eseguita solo tramite un componente "sanitizer dei log" che garantisca che tutti i log vengano sottoposti a sanitizzazione prima di essere stampati, come mostrato nello snippet di codice seguente.
Kotlin
data class ToMask<T>(private val data: T) {
// Prevents accidental logging when an error is encountered.
override fun toString() = "XX"
// Makes it more difficult for developers to invoke sensitive data
// and facilitates sensitive data usage tracking.
fun getDataToMask(): T = data
}
data class Person(
val email: ToMask<String>,
val username: String
)
fun main() {
val person = Person(
ToMask("name@gmail.com"),
"myname"
)
println(person)
println(person.email.getDataToMask())
}
Java
public class ToMask<T> {
// Prevents accidental logging when an error is encountered.
public String toString(){
return "XX";
}
// Makes it more difficult for developers to invoke sensitive data
// and facilitates sensitive data usage tracking.
public T getDataToMask() {
return this;
}
}
public class Person {
private ToMask<String> email;
private String username;
public Person(ToMask<String> email, String username) {
this.email = email;
this.username = username;
}
}
public static void main(String[] args) {
Person person = new Person(
ToMask("name@gmail.com"),
"myname"
);
System.out.println(person);
System.out.println(person.email.getDataToMask());
}