OWASP-Kategorie:MASVS-STORAGE: Storage
Übersicht
Offenlegung von Protokollinformationen ist eine Art von Sicherheitslücke, bei der Apps sensible Daten in das Geräteprotokoll schreiben. Wenn diese vertraulichen Informationen in die Hände von Angreifern gelangen, können sie direkt wertvoll sein, z. B. Anmeldedaten oder personenidentifizierbare Informationen, oder weitere Angriffe ermöglichen.
Dieses Problem kann in den folgenden Szenarien auftreten:
- Von der App generierte Logs:
- Die Logs ermöglichen absichtlich den Zugriff für unbefugte Akteure, enthalten aber versehentlich sensible Daten.
- Die Logs enthalten absichtlich vertrauliche Daten, sind aber versehentlich für unbefugte Akteure zugänglich.
- Allgemeine Fehlerlogs, in denen je nach ausgelöster Fehlermeldung manchmal vertrauliche Daten ausgegeben werden.
- Extern generierte Logs:
- Externe Komponenten sind für das Drucken von Protokollen mit sensiblen Daten verantwortlich.
Android-Log.*-Anweisungen schreiben in den gemeinsamen Speicherpuffer logcat. Seit Android 4.1 (API-Level 16) kann nur privilegierten System-Apps der Zugriff zum Lesen von logcat gewährt werden, indem die Berechtigung READ_LOGS deklariert wird. Android unterstützt jedoch eine Vielzahl von Geräten, deren vorinstallierte Anwendungen manchmal das READ_LOGS-Privileg deklarieren. Daher wird davon abgeraten, direkt in logcat zu protokollieren, da dies anfälliger für Datenlecks ist.
Achten Sie darauf, dass alle Protokollierungen in logcat in Nicht-Debug-Versionen Ihrer Anwendung bereinigt werden. Entfernen Sie alle Daten, die möglicherweise vertraulich sind. Als zusätzliche Vorsichtsmaßnahme sollten Sie Tools wie R8 verwenden, um alle Protokollebenen außer „Warnung“ und „Fehler“ zu entfernen. Wenn Sie detailliertere Logs benötigen, verwenden Sie den internen Speicher und verwalten Sie Ihre eigenen Logs direkt, anstatt das Systemlog zu verwenden.
Auswirkungen
Der Schweregrad der Sicherheitslücke „Offenlegung von Protokollinformationen“ kann je nach Kontext und Art der vertraulichen Daten variieren. Insgesamt führt diese Sicherheitslücke zu einem Verlust der Vertraulichkeit potenziell kritischer Informationen wie personenidentifizierbarer Informationen und Anmeldedaten.
Gegenmaßnahmen
Allgemein
Als allgemeine präventive Maßnahme während des Designs und der Implementierung sollten Sie Vertrauensgrenzen gemäß dem Prinzip der geringsten Berechtigung ziehen. Idealerweise sollten sensible Daten keine der Vertrauensbereiche überschreiten oder erreichen. Dadurch wird die Trennung von Berechtigungen verstärkt.
Vertrauliche Daten nicht protokollieren. Protokollieren Sie nach Möglichkeit nur Kompilierzeitkonstanten. Sie können das ErrorProne-Tool für die Annotation von Konstanten zur Kompilierzeit verwenden.
Vermeiden Sie Logs, in denen Anweisungen ausgegeben werden, die je nach ausgelöstem Fehler unerwartete Informationen, einschließlich sensibler Daten, enthalten können. Die in Logs und Fehlerlogs ausgegebenen Daten sollten möglichst nur vorhersehbare Informationen enthalten.
Vermeiden Sie das Logging in logcat. Das liegt daran, dass die Protokollierung in logcat aufgrund von Apps mit der Berechtigung READ_LOGS zu einem Datenschutzproblem werden kann. Außerdem ist sie ineffektiv, da sie keine Benachrichtigungen auslösen oder abgefragt werden kann. Wir empfehlen, das logcat-Backend nur für Entwickler-Builds zu konfigurieren.
Die meisten Bibliotheken für die Log-Verwaltung ermöglichen das Definieren von Log-Ebenen, sodass in Debug- und Produktionslogs unterschiedliche Mengen an Informationen protokolliert werden können. Ändern Sie die Protokollebene, sodass sie sich von „debug“ unterscheidet, sobald die Produkttests beendet sind.
Entfernen Sie so viele Protokollebenen wie möglich aus der Produktion. Wenn Sie nicht vermeiden können, Logs in der Produktionsumgebung zu speichern, entfernen Sie nicht konstante Variablen aus den Log-Anweisungen. Folgende Szenarien sind möglich:
- Sie können alle Logs aus der Produktion entfernen.
- Sie müssen Warn- und Fehlerlogs in der Produktionsumgebung aufbewahren.
In beiden Fällen sollten Sie Protokolle automatisch mit Bibliotheken wie R8 entfernen. Alle Versuche, Protokolle manuell zu entfernen, sind fehleranfällig. Im Rahmen der Codeoptimierung kann R8 so konfiguriert werden, dass Protokollebenen sicher entfernt werden, die Sie für die Fehlerbehebung beibehalten, aber in der Produktion entfernen möchten.
Wenn Sie in der Produktion protokollieren, bereiten Sie Flags vor, mit denen Sie das Logging im Falle eines Vorfalls bedingt herunterfahren können. Bei Flags für die Incident Response sollten folgende Aspekte priorisiert werden: Sicherheit der Bereitstellung, Geschwindigkeit und Einfachheit der Bereitstellung, Gründlichkeit der Protokollbearbeitung, Arbeitsspeichernutzung und Leistungskosten für das Scannen jedes Logeintrags.
Mit R8 Logs für logcat aus Produktions-Builds entfernen
In Android Studio 3.4 oder dem Android-Gradle-Plug-in 3.4.0 und höher ist R8 der Standardcompiler für die Codeoptimierung und ‑verkleinerung. Sie müssen jedoch R8 aktivieren.
R8 hat ProGuard ersetzt, aber die Regeldatei im Stammordner des Projekts heißt weiterhin proguard-rules.pro.Das folgende Snippet zeigt ein Beispiel für eine proguard-rules.pro-Datei, in der alle Logs aus der Produktion mit Ausnahme von Warnungen und Fehlern entfernt werden:
-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");
}
Im folgenden Beispiel für die Datei proguard-rules.pro werden alle Logs aus der Produktion entfernt:
-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");
}
R8 bietet Funktionen zum Verkleinern von Apps und zum Entfernen von Protokollen. Wenn Sie R8 nur für die Funktion zum Entfernen von Protokollen verwenden möchten, fügen Sie der Datei proguard-rules.pro Folgendes hinzu:
-dontwarn **
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!code/allocation/variable
-keep class **
-keepclassmembers class *{*;}
-keepattributes *
Eventuelle Logs in der Produktion, die vertrauliche Daten enthalten, bereinigen
Damit keine sensiblen Daten offengelegt werden, muss das gesamte Logging in logcat in Nicht-Debug-Versionen Ihrer Anwendung bereinigt werden. Entfernen Sie alle Daten, die möglicherweise vertraulich sind.
Beispiel:
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);
}
}
Sensible Daten in Logs entfernen
Wenn Sie vertrauliche Daten in Ihre Logs aufnehmen müssen, empfehlen wir, die Logs vor dem Drucken zu bereinigen, um vertrauliche Daten zu entfernen oder zu verschleiern. Verwenden Sie dazu eine der folgenden Methoden:
- Tokenisierung: Wenn sensible Daten in einem Vault gespeichert sind, z. B. in einem Verschlüsselungsverwaltungssystem, auf das über Tokens verwiesen werden kann, protokollieren Sie das Token anstelle der sensiblen Daten.
- Datenmaskierung: Die Datenmaskierung ist ein irreversibler Prozess. Es wird eine Version der sensiblen Daten erstellt, die strukturell dem Original ähnelt, aber die sensibelsten Informationen in einem Feld verbirgt. Beispiel: Die Kreditkartennummer
1234-5678-9012-3456wird durchXXXX-XXXX-XXXX-1313ersetzt. Bevor Sie Ihre App in der Produktion veröffentlichen, empfehlen wir Ihnen, eine Sicherheitsprüfung durchzuführen, um die Verwendung der Datenmaskierung zu untersuchen. Warnung:Verwenden Sie die Datenmaskierung nicht in Fällen, in denen bereits die Offenlegung eines Teils der vertraulichen Daten die Sicherheit erheblich beeinträchtigen kann, z. B. bei der Verarbeitung von Passwörtern. - Entfernen: Die Schwärzung ähnelt der Maskierung, aber es werden alle Informationen in einem Feld ausgeblendet. Beispiel: Die Kreditkartennummer
1234-5678-9012-3456wird durchXXXX-XXXX-XXXX-XXXXersetzt. - Filtern: Implementieren Sie Formatstrings in der von Ihnen gewählten Logging-Bibliothek, falls sie noch nicht vorhanden sind, um die Änderung von nicht konstanten Werten in Log-Anweisungen zu ermöglichen.
Das Drucken von Logs sollte nur über eine „Logs Sanitizer“-Komponente erfolgen, die dafür sorgt, dass alle Logs bereinigt werden, bevor sie gedruckt werden, wie im folgenden Code-Snippet gezeigt.
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());
}