Раскрытие информации в журнале

Категория OWASP: MASVS-STORAGE: Хранилище

Обзор

Раскрытие информации журнала — это тип уязвимости, при которой приложения записывают конфиденциальные данные в журнал устройства. Если эта конфиденциальная информация будет предоставлена ​​злоумышленникам, она может оказаться ценной сразу же (например, учетные данные пользователя или личная информация (PII)) или же может привести к дальнейшим атакам.

Эта проблема может возникнуть в любом из следующих сценариев:

  • Журналы, созданные приложением:
    • Журналы намеренно предоставляют доступ неавторизованным лицам, но случайно содержат конфиденциальные данные.
    • Журналы намеренно содержат конфиденциальные данные, но они случайно становятся доступными неавторизованным лицам.
    • Общие журналы ошибок, которые иногда могут печатать конфиденциальные данные, в зависимости от вызванного сообщения об ошибке.
  • Внешние журналы:
    • Внешние компоненты отвечают за печать журналов, содержащих конфиденциальные данные.

Операторы Android Log.* записывают в общий буфер памяти logcat . Начиная с Android 4.1 (уровень API 16), доступ к чтению logcat можно предоставить только привилегированным системным приложениям, объявив разрешение READ_LOGS . Однако Android поддерживает невероятно разнообразный набор устройств, предварительно загруженные приложения которых иногда декларируют привилегию READ_LOGS . Как следствие, ведение журналов непосредственно в logcat не рекомендуется, поскольку это более подвержено утечке данных.

Убедитесь, что все журналы в logcat очищены в неотладочных версиях вашего приложения. Удалите все данные, которые могут быть конфиденциальными. В качестве дополнительной меры предосторожности используйте такие инструменты, как R8, для удаления всех уровней журнала, кроме предупреждений и ошибок. Если вам нужны более подробные журналы, используйте внутреннюю память и управляйте собственными журналами напрямую, а не системным журналом.

Влияние

Серьезность класса уязвимости раскрытия информации журнала может варьироваться в зависимости от контекста и типа конфиденциальных данных. В целом, влияние этого класса уязвимости заключается в потере конфиденциальности потенциально критической информации, такой как личные данные и учетные данные.

Смягчения

Общий

В качестве общей упреждающей меры во время проектирования и реализации нарисуйте границы доверия в соответствии с принципом наименьших привилегий . В идеале конфиденциальные данные не должны пересекать или выходить за пределы зон доверия. Это усиливает разделение привилегий.

Не регистрируйте конфиденциальные данные. По возможности регистрируйте только константы времени компиляции. Вы можете использовать инструмент ErrorProne для аннотации констант времени компиляции.

Избегайте журналов, в которых печатаются операторы, которые могут содержать непредвиденную информацию, включая конфиденциальные данные, в зависимости от возникшей ошибки. Насколько это возможно, данные, печатаемые в журналах и журналах ошибок, должны включать только предсказуемую информацию.

Избегайте входа в logcat . Это связано с тем, что вход в logcat может стать проблемой конфиденциальности из-за приложений с разрешением READ_LOGS . Это также неэффективно, поскольку не может вызывать оповещения или запрашиваться. Мы рекомендуем приложениям настраивать серверную часть logcat только для сборок разработчиков.

Большинство библиотек управления журналами позволяют определять уровни журналов, что позволяет регистрировать различные объемы информации между журналами отладки и рабочими журналами. Измените уровень журнала, чтобы он отличался от «отладки», как только тестирование продукта закончится.

Удалите из рабочей среды как можно больше уровней журналов. Если вы не можете избежать ведения журналов в рабочей среде, удалите непостоянные переменные из операторов журнала. Могут возникнуть следующие сценарии:

  • Вы можете удалить все журналы из производства.
  • Вам необходимо вести журналы предупреждений и ошибок в производстве.

В обоих случаях автоматически удаляйте журналы с помощью таких библиотек, как R8. Любые попытки удалить журналы вручную могут привести к ошибкам. В рамках оптимизации кода R8 можно настроить на безопасное удаление уровней журнала, которые вы хотите сохранить для отладки, но удалить в рабочей среде.

Если вы собираетесь войти в производственную систему, подготовьте флаги, которые вы сможете использовать для условного прекращения ведения журнала в случае возникновения инцидента. Флаги реагирования на инциденты должны иметь приоритет: безопасность развертывания; скорость и простота развертывания, тщательность редактирования журналов, использование памяти и затраты на производительность при сканировании каждого сообщения журнала.

Удаление журналов в logcat из производственных сборок с помощью R8.

В Android Studio 3.4 или плагине Android Gradle 3.4.0 и выше R8 является компилятором по умолчанию для оптимизации и сжатия кода. Однако вам необходимо включить R8 .

R8 заменил ProGuard, но файл правил в корневой папке проекта по-прежнему называется proguard-rules.pro . В следующем фрагменте показан пример файла proguard-rules.pro , который удаляет из рабочей среды все журналы, кроме предупреждений и ошибок:

-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");
}

В следующем примере файла proguard-rules.pro удаляются все журналы из рабочей среды:

-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 предоставляет возможности сокращения приложений и функции удаления журналов. Если вы хотите использовать R8 только для функции удаления журналов, добавьте в файл proguard-rules.pro следующее:

-dontwarn **
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose

-optimizations !code/simplification/arithmetic,!code/allocation/variable
-keep class **
-keepclassmembers class *{*;}
-keepattributes *

Очистите все возможные журналы в рабочей среде, содержащие конфиденциальные данные.

Чтобы избежать утечки конфиденциальных данных, убедитесь, что все журналы в logcat очищены в неотладочных версиях вашего приложения. Удалите все данные, которые могут быть конфиденциальными.

Пример:

Котлин

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)
    }
}

Ява

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);
   }
}

Редактировать конфиденциальные данные в журналах

Если вам необходимо включить конфиденциальные данные в свои журналы, мы рекомендуем очистить журналы перед их печатью, чтобы удалить или скрыть конфиденциальные данные. Для этого используйте один из следующих методов:

  • Токенизация. Если конфиденциальные данные хранятся в хранилище, например в системе управления шифрованием, из которой можно ссылаться на секреты через токены, регистрируйте токен вместо конфиденциальных данных.
  • Маскирование данных. Маскирование данных — это односторонний необратимый процесс. Он создает версию конфиденциальных данных, которая структурно похожа на оригинал, но скрывает наиболее конфиденциальную информацию, содержащуюся в поле. Пример: замена номера кредитной карты 1234-5678-9012-3456 на XXXX-XXXX-XXXX-1313 . Прежде чем выпустить приложение в рабочую среду, мы рекомендуем вам выполнить процедуру проверки безопасности, чтобы тщательно изучить использование маскировки данных. Предупреждение. Не используйте маскирование данных в тех случаях, когда даже раскрытие только части конфиденциальных данных может существенно повлиять на безопасность, например при обработке паролей.
  • Редакция. Редактирование похоже на маскирование, но скрывает всю информацию, содержащуюся в поле. Пример: замена номера кредитной карты 1234-5678-9012-3456 на XXXX-XXXX-XXXX-XXXX .
  • Фильтрация. Внедрите строки формата в выбранную вами библиотеку журналирования, если они еще не существуют, чтобы облегчить изменение непостоянных значений в операторах журнала.

Печать журналов следует выполнять только с помощью компонента «дезинфицирующего средства журналов», который обеспечивает очистку всех журналов перед печатью, как показано в следующем фрагменте кода.

Котлин

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())
}

Ява

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());
}