OWASP-Kategorie: MASVS-STORAGE: Speicher
Übersicht
Die Offenlegung von Protokollinformationen ist eine Art von Sicherheitslücke, bei der Apps sensible Daten in das Geräteprotokoll drucken. Wenn diese sensiblen Daten in die Hände von böswilligen Akteuren gelangen, können sie direkt wertvoll sein, z. B. Anmeldedaten oder personenidentifizierbare Informationen (PII), oder sie können weitere Angriffe ermöglichen.
Dieses Problem kann in den folgenden Fällen auftreten:
- Von der App generierte Protokolle:
- Die Protokolle gewähren zwar absichtlich den Zugriff für nicht autorisierte Akteure, enthalten aber versehentlich sensible Daten.
- Die Protokolle enthalten absichtlich vertrauliche Daten, auf die aber versehentlich unbefugte Personen zugreifen können.
- Generische Fehlerprotokolle, in denen je nach ausgelöster Fehlermeldung manchmal vertrauliche Daten gedruckt werden.
- Extern generierte Protokolle:
- Externe Komponenten sind für das Drucken von Protokollen mit sensiblen Daten verantwortlich.
Android-Log.*
-Anweisungen schreiben in den gemeinsamen Speicherbuffer logcat
. Seit Android 4.1 (API-Level 16) können nur privilegierte System-Apps Lesezugriff auf logcat
erhalten, indem die Berechtigung READ_LOGS
deklariert wird. Android unterstützt jedoch eine unglaublich vielfältige Auswahl an Geräten, deren vorinstallierte Apps manchmal das READ_LOGS
-Berechtigungs-Attribut deklarieren. Daher wird davon abgeraten, direkt in logcat
zu loggen, da dies anfälliger für Datenlecks ist.
Achten Sie darauf, dass alle Protokolle in logcat
in nicht debugfähigen Versionen Ihrer Anwendung bereinigt werden. Entfernen Sie alle Daten, die potenziell vertraulich sind. Verwenden Sie als zusätzliche Vorsichtsmaßnahme Tools wie R8, um alle Logebenen außer „Warnung“ und „Fehler“ zu entfernen. Wenn Sie detailliertere Protokolle benötigen, verwenden Sie den internen Speicher und verwalten Sie Ihre eigenen Protokolle direkt, anstatt das Systemprotokoll zu verwenden.
Positiv beeinflussen
Die Schwere der Sicherheitslückenklasse „Offenlegung von Protokollinformationen“ kann je nach Kontext und Art der sensiblen Daten variieren. Insgesamt besteht bei dieser Sicherheitslückenklasse die Gefahr, dass vertrauliche Informationen wie personenidentifizierbare Informationen und Anmeldedaten offengelegt werden.
Abhilfemaßnahmen
Allgemein
Als allgemeine vorbeugende Maßnahme sollten Sie beim Entwerfen und Implementieren Vertrauensgrenzen gemäß dem Prinzip der geringsten Berechtigung festlegen. Idealerweise sollten vertrauliche Daten keine der Vertrauensbereiche überschreiten oder verlassen. Dies verstärkt die Trennung der Berechtigungen.
Vertrauliche Daten dürfen nicht protokolliert werden. Protokollieren Sie nach Möglichkeit nur Compilezeitkonstanten. Sie können das ErrorProne-Tool für die Annotation von Konstanten zur Kompilierungszeit verwenden.
Vermeiden Sie Protokolle, die Anweisungen drucken, die je nach ausgelöstem Fehler unerwartete Informationen enthalten können, einschließlich vertraulicher Daten. Die in Protokollen und Fehlerprotokollen gedruckten Daten sollten nach Möglichkeit nur vorhersehbare Informationen enthalten.
Vermeiden Sie das Logging in logcat
. Das liegt daran, dass die Anmeldung in logcat
aufgrund von Apps mit der Berechtigung READ_LOGS
zu Datenschutzproblemen führen kann. Außerdem ist es ineffektiv, da damit keine Benachrichtigungen ausgelöst und keine Abfragen durchgeführt werden können. Wir empfehlen, das logcat
-Backend nur für Entwicklerbuilds zu konfigurieren.
Die meisten Log-Verwaltungsbibliotheken ermöglichen das Definieren von Log-Ebenen, wodurch unterschiedliche Informationen in Debug- und Produktionsprotokollen protokolliert werden können. Ändern Sie die Protokollebene, sobald die Produkttests abgeschlossen sind, sodass sie sich von „debug“ unterscheidet.
Entfernen Sie so viele Protokollebenen wie möglich aus der Produktion. Wenn Sie Logs in der Produktionsumgebung nicht vermeiden können, entfernen Sie nicht konstante Variablen aus den Log-Anweisungen. Die folgenden Szenarien können auftreten:
- Sie können alle Protokolle aus der Produktion entfernen.
- Sie müssen Warn- und Fehlerprotokolle in der Produktionsumgebung aufbewahren.
Entfernen Sie in beiden Fällen Protokolle automatisch mithilfe von Bibliotheken wie R8. Versuche, Protokolle manuell zu entfernen, sind anfällig für Fehler. Im Rahmen der Codeoptimierung kann R8 so konfiguriert werden, dass Protokollebenen, die Sie für die Fehlerbehebung beibehalten möchten, in der Produktion sicher entfernt werden.
Wenn Sie Protokolle in der Produktion erfassen möchten, sollten Sie Flags vorbereiten, mit denen Sie die Protokollierung bei einem Vorfall bedingt beenden können. Bei Flags für die Reaktion auf Vorfälle sollten folgende Aspekte priorisiert werden: Sicherheit der Bereitstellung, Geschwindigkeit und Einfachheit der Bereitstellung, Gründlichkeit der Entfernung von Protokollen, Speichernutzung und Leistungskosten beim Scannen jeder Protokollnachricht.
Entfernen Sie mit R8 Logs aus Produktionsbuilds, die in logcat ausgegeben werden.
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 -minimierung. Sie müssen jedoch R8 aktivieren.
ProGuard wurde durch R8 ersetzt, aber die Regelndatei im Stammverzeichnis des Projekts heißt weiterhin proguard-rules.pro
.Im folgenden Snippet sehen Sie eine Beispielproguard-rules.pro
-Datei, in der alle Protokolle aus der Produktion entfernt werden, außer Warnungen und Fehler:
-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");
}
In der folgenden Beispieldatei proguard-rules.pro
werden alle Protokolle aus der Produktionsumgebung 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 Minimieren 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 proguard-rules.pro
-Datei Folgendes hinzu:
-dontwarn **
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!code/allocation/variable
-keep class **
-keepclassmembers class *{*;}
-keepattributes *
Alle Logs in der Produktion, die vertrauliche Daten enthalten, bereinigen
Damit keine sensiblen Daten preisgegeben werden, müssen alle Protokolle in logcat
in nicht-debugfähigen Versionen Ihrer Anwendung bereinigt werden. Entfernen Sie alle Daten, die potenziell 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 aus Protokollen entfernen
Wenn Sie vertrauliche Daten in Ihren Protokollen angeben müssen, empfehlen wir, die Protokolle vor dem Drucken zu bereinigen, um vertrauliche Daten zu entfernen oder unkenntlich zu machen. Verwenden Sie dazu eine der folgenden Methoden:
- Tokenisierung Wenn sensible Daten in einem Tresor gespeichert werden, z. B. in einem Verschlüsselungsverwaltungssystem, auf das über Tokens verwiesen werden kann, loggen Sie das Token anstelle der sensiblen Daten.
- Datenmaskierung Die Datenmaskierung ist ein irreversibler Prozess. Dabei wird eine Version der sensiblen Daten erstellt, die strukturell dem Original ähnelt, aber die sensibelsten Informationen in einem Feld ausblendet. Beispiel: Die Kreditkartennummer
1234-5678-9012-3456
wird durchXXXX-XXXX-XXXX-1313
ersetzt. Bevor Sie Ihre App in die Produktion bringen, empfehlen wir Ihnen, eine Sicherheitsüberprüfung durchzuführen, um die Verwendung der Datenmaskierung zu prüfen. Warnung:Verwenden Sie die Datenmaskierung nicht, wenn selbst die Freigabe eines Teils der vertraulichen Daten die Sicherheit erheblich beeinträchtigen kann, z. B. beim Umgang mit Passwörtern. - Entfernen Das Entfernen von Daten ähnelt dem Maskieren, bei dem jedoch alle Informationen in einem Feld ausgeblendet werden. Beispiel: Die Kreditkartennummer
1234-5678-9012-3456
wird durchXXXX-XXXX-XXXX-XXXX
ersetzt. - Filterung Implementieren Sie Formatstrings in Ihrer bevorzugten Logging-Bibliothek, falls diese noch nicht vorhanden sind, um die Änderung nicht konstanter Werte in Protokollanweisungen zu erleichtern.
Das Drucken von Protokollen sollte nur über eine Komponente „Logs Sanitizer“ erfolgen, die dafür sorgt, dass alle Protokolle vor dem Drucken bereinigt 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());
}