Перейти к содержанию

Запись в произвольный файл через ContentResolver

Описание

ContentResolver предоставляет доступ к данным различных ContentProvider, позволяя взаимодействовать с ними и выполнять операции чтения, записи, обновления и удаления. Метод openOutputStream() может использоваться для записи данных в файл через ContentProvider. Если Uri, переданный в метод, не проверяется должным образом, это может привести к записи данных в произвольный файл, что создаёт серьёзную угрозу безопасности.

Пример использования openOutputStream() для записи данных:

Uri uri = getIntent().getParcelableExtra("file_uri");
try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
    if (outputStream != null) {
        outputStream.write("Some data".getBytes());
    }
} catch (IOException e) {
    e.printStackTrace();
}

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

Проблема

Если сторонние данные используются без должной проверки при вызове метода openOutputStream(), это может привести к следующим проблемам:

  1. Перезапись критических файлов — Злоумышленник может передать URI, указывающий на критически важные файлы системы или приложения, что может привести к их перезаписи или повреждению.
  2. Эскалация привилегий — При некорректной настройке разрешений ContentProvider злоумышленники могут получить доступ к файлам, которые они не должны видеть или изменять, и записать в них произвольные данные.
  3. Нарушение конфиденциальности данных — Злоумышленник может произвольно записывать данные в файлы, создавая угрозу безопасности и конфиденциальности данных.

Рекомендации

  1. Проверяйте URI перед использованием: Перед тем как использовать Uri для открытия потока вывода через openOutputStream(), убедитесь, что Uri безопасен. Это можно сделать с помощью проверки, относится ли Uri к разрешенному ContentProvider, и проверкой его схемы (например, content://trusted-authority/).

    Пример безопасной проверки URI:

    Uri uri = getIntent().getParcelableExtra("file_uri");
    if (uri != null && "content".equals(uri.getScheme()) && "trusted-authority".equals(uri.getAuthority())) {
        try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
            if (outputStream != null) {
                outputStream.write("Some data".getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    } else {
        Log.e("ContentResolver", "Неразрешенный URI");
    }
    

    В этом примере Uri проверяется на соответствие схеме content и проверяется, принадлежит ли он доверенному источнику.

  2. Используйте ContentProvider с ограниченными правами: Если ваш ContentProvider должен быть доступен другим приложениям, убедитесь, что вы ограничиваете операции, которые могут быть выполнены через него. Для этого используйте атрибуты android:exported, android:readPermission, и android:writePermission в AndroidManifest.xml.

  3. Используйте белые списки разрешенных URI: Разрешайте только те URI, которые соответствуют заранее определенному списку разрешенных значений. Это исключает возможность использования произвольных URI для записи данных.

  4. Проверяйте права доступа: Перед записью в файл убедитесь, что ваше приложение имеет все необходимые права доступа. Например, проверяйте наличие разрешений на запись в ContentProvider с помощью метода checkCallingOrSelfUriPermission().

    Пример проверки прав доступа:

    if (getContentResolver().checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED) {
        // Запись разрешена
    } else {
        Log.e("ContentResolver", "Недостаточно прав для записи в указанный URI");
    }
    
  5. Избегайте использования открытых URI для критических файлов: Избегайте использования URI, которые могут указывать на чувствительные файлы или системные данные. Ограничьте возможность передачи таких URI, если они не нужны для работы вашего приложения.

Дополнительные примеры

Пример небезопасного использования метода openOutputStream() без проверки URI:

public void writeToUri(Uri uri, String data) {
    try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
        if (outputStream != null) {
            outputStream.write(data.getBytes());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

В данном примере отсутствует проверка URI, что позволяет злоумышленнику передать произвольный URI и записать данные в неразрешенный файл.

Ссылки

  1. Основы ContentProvider
  2. ContentResolver и работа с данными
  3. Работа с URI и безопасность
К началу