← Назад к вопросам

Где хранить токены?

1.7 Middle🔥 241 комментариев
#Работа с данными#Сетевое взаимодействие

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Где хранить токены аутентификации в Android-приложении

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

Недопустимые способы хранения

Сначала перечислю категорически неприемлемые места:

  • В SharedPreferences без шифрования — файлы легко доступны на рутированном устройстве.
  • В Intent как extras — могут попасть в логи или быть перехвачены.
  • В статических переменных класса — данные теряются при убийстве процесса и небезопасны.
  • В SQLite базе без шифрования — аналогично SharedPreferences.
  • В виде константы в коде — токен одинаков для всех пользователей и становится доступен при декомпиляции.

Рекомендуемые способы хранения

1. Android Keystore System + EncryptedSharedPreferences (API 23+)

Наиболее предпочтительный современный подход. Keystore предоставляет аппаратно-защищенное хранилище криптографических ключей, которые практически невозможно извлечь. Связка EncryptedSharedPreferences (из библиотеки Security Crypto) автоматически использует Keystore для шифрования данных.

import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys

val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)

val sharedPrefs = EncryptedSharedPreferences.create(
    "secret_shared_prefs",
    masterKeyAlias,
    context,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

// Запись токена
sharedPrefs.edit().putString("ACCESS_TOKEN", "your.jwt.token").apply()

// Чтение токена
val token = sharedPrefs.getString("ACCESS_TOKEN", null)

Преимущества:

  • Высокий уровень безопасности.
  • Ключи хранятся в доверенной среде (TEE/SE).
  • Простой API, похожий на обычные SharedPreferences.
  • Не требует ручного управления шифрованием.

2. Библиотека Security Crypto (DataStore)

Для современных приложений, использующих реактивный подход и Kotlin coroutines/Flow, можно использовать EncryptedDataStore.

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.security.crypto.EncryptedDataStore

val dataStore: DataStore<Preferences> = EncryptedDataStore.create(
    context,
    "tokens.pb"
)

suspend fun saveToken(token: String) {
    dataStore.edit { preferences ->
        preferences[ACCESS_TOKEN_KEY] = token
    }
}

val accessTokenFlow: Flow<String?> = dataStore.data
    .map { preferences -> preferences[ACCESS_TOKEN_KEY] }

3. Биометрическая аутентификация + Keystore

Для максимальной безопасности, когда доступ к токену должен быть защищен биометрией или PIN-кодом пользователя.

Принцип работы:

  1. Создается ключ в Android Keystore с настройкой setUserAuthenticationRequired(true).
  2. Токен шифруется с помощью этого ключа и сохраняется в SharedPreferences.
  3. Для расшифровки необходимо пройти биометрическую аутентификацию, которая "разблокирует" ключ.

Этот подход идеален для хранения Refresh Token или других наиболее чувствительных данных.

Архитектурные рекомендации

  1. Разделение токенов: Access Token (короткоживущий) можно хранить в памяти (ViewModel), а Refresh Token (долгоживущий) — в максимально защищенном хранилище (Keystore с биометрией).
  2. Использование Dependency Injection: Создайте интерфейс TokenStorage для инверсии зависимостей. Это упростит тестирование и возможную смену реализации.
  3. Очистка токенов: Всегда предусматривайте механизм безопасного удаления токенов при логауте.
  4. Миграция: Если в приложении уже используется незащищенное хранение, реализуйте поэтапную миграцию на безопасное.
// Пример интерфейса для безопасного хранилища
interface SecureTokenStorage {
    suspend fun saveAccessToken(token: String)
    suspend fun getAccessToken(): String?
    suspend fun saveRefreshToken(token: String)
    suspend fun getRefreshToken(): String?
    suspend fun clearTokens()
}

// Реализация через EncryptedSharedPreferences
class EncryptedTokenStorage @Inject constructor(
    private val context: Context
) : SecureTokenStorage {
    // ... реализация методов
}

Вывод и итоговая стратегия

Текущий best practice для новых приложений:

  • API 23+ (Android 6.0+): Используйте EncryptedSharedPreferences из библиотеки androidx.security:security-crypto. Это самый простой и безопасный вариант по умолчанию.
  • Для критически важных данных (банкинг, мед.приложения): Добавьте слой биометрической аутентификации поверх Keystore.
  • Архитектура: Инкапсулируйте логику работы с токенами в отдельный компонент (репозиторий, DataSource), реализующий интерфейс TokenStorage.

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