EncryptedSharedPreferences — е компонент на библиотеката AndroidX Security, осигуряващ прозрачно криптиране на данни, запазвани чрез SharedPreferences API. За разлика от обикновените SharedPreferences, където данните се съхраняват в отворен XML файл, EncryptedSharedPreferences автоматично криптира ключовете и стойностите преди запис на диска. Според Android Developers, библиотеката използва AES-256 GCM за стойности и AES-256 SIV (RFC 5297) за ключове, осигурявайки поверителност и цялостност на данните.
Основни точки
EncryptedSharedPreferences — е клас от пакета androidx.security.crypto, представен в AndroidX Security 1.0.0 (2019). Той имплементира интерфейса SharedPreferences, но всички операции за запис (putString, putInt, putBoolean и т.н.) предварително криптират данните, а операциите за четене ги декриптират преди връщане.
Стандартните SharedPreferences записват данни в XML файл в директорията на приложението (/data/data/package/shared_prefs/). Файлът не е криптиран — при root достъп до устройството или при анализ на резервно копие, всички данни се четат като обикновен XML. Токени за удостоверяване, API ключове, лични данни на потребителя стават достъпни за нападателя.
EncryptedSharedPreferences решава този проблем на ниво библиотека: данните се криптират преди запис на диска и се декриптират при четене. Разработчикът не трябва ръчно да извиква криптографски функции — API остава идентичен с обикновените SharedPreferences.
Библиотеката AndroidX Security v1.0.0 излезе през декември 2019. EncryptedSharedPreferences замени остарелия подход с ръчно криптиране чрез Cipher + SharedPreferences. Текущата стабилна версия е 1.1.0-alpha06 (2024), поддържаща API 19+. Библиотеката е част от Jetpack и не изисква допълнителни разрешения.
Според Google Security Blog (2024), EncryptedSharedPreferences е препоръчителният начин за съхранение на поверителни настройки на приложението, които не изискват синхронизация чрез облак. За по-сложни сценарии се предлага Room с криптиране чрез SQLCipher.
EncryptedSharedPreferences използва двустепенна схема на криптиране: главен ключ (Master Key) се съхранява в Android Keystore, а за криптиране на данните се използват производни ключове. Това е комбинация от защита на Keystore и производителност на симетричното криптиране.
За стойности се използва AES-256 GCM (Galois/Counter Mode) — режим на удостоверено криптиране (AEAD), осигуряващ поверителност и цялостност на данните. За ключове (имена на параметри) се прилага AES-256 SIV (RFC 5297) — детерминистично криптиране, необходимо за търсене по ключ без разкриване на съдържанието му.
Всеки файл EncryptedSharedPreferences съдържа криптирани двойки ключ-стойност. Структура на файла: първо заглавна част с метаданни (версия, идентификатор на ключ), след това списък с криптирани записи. Файлът не е валиден XML и не се чете от текстови редактори.
Класът MasterKey отговаря за създаването и управлението на 256-битовия главен ключ, който се съхранява в Android Keystore. MasterKey.Builder позволява конфигуриране: тип съхранение (Keystore или софтуерно), защита с биометрия, време на живот на ключа. По подразбиране главният ключ се генерира в Android Keystore с алгоритъм AES/GCM/NoPadding.
import androidx.security.crypto.MasterKey
import androidx.security.crypto.EncryptedSharedPreferences
fun getEncryptedPrefs() {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.AES256_GCM_SPEC)
.build()
val prefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
EncryptedSharedPreferences.create приема пет параметъра: контекст, име на файл, главен ключ, схема за криптиране на ключове и схема за криптиране на стойности. Изборът на схеми влияе върху производителността и нивото на защита.
AES256_SIV — детерминистично криптиране: идентични ключове винаги дават идентичен криптиран текст. Това е необходимо за търсене по ключ (SharedPreferences.getX(key)). Недостатък: нападателят може да определи кои ключове се използват по повтарящите се криптотекстове. AES256_SIV2 — подобрена версия с допълнителна рандомизация.
За стойности се използва AES256_GCM. GCM добавя 12-байтов IV (инициализиращ вектор) и 16-байтов удостоверителен маркер към всяка стойност. Това осигурява поверителност (никой не може да прочете стойността) и удостоверяване (никой не може да промени стойността без откриване).
Методът setUserAuthenticationRequired(true) в MasterKey.Builder изисква биометрично потвърждение преди получаване на главния ключ от Keystore. Това добавя допълнително ниво: дори ако приложението работи на отключено устройство, нападателят не може да прочете EncryptedSharedPreferences без Face ID или Touch ID.
Важно: при setUserAuthenticationRequired главният ключ става недостъпен, ако потребителят е променил или изтрил биометрията. Необходимо е да се обработи KeyPermanentlyInvalidatedException и да се създаде нов главен ключ с миграция на данните.
fun createBiometricKey(): MasterKey {
return MasterKey.Builder(context)
.setKeyScheme(MasterKey.AES256_GCM_SPEC)
.setUserAuthenticationRequired(true)
.setRequestStrongBoxBacked(true)
.build()
}
fun writeSecureToken(token: String) {
try {
prefs.edit().putString("auth_token", token).apply()
} catch (e: KeyPermanentlyInvalidatedException) {
// Биометрията се промени — трябва да се пресъздаде ключът
}
}
Нека разгледаме пълен пример за интеграция на EncryptedSharedPreferences в Android приложение на Kotlin. Библиотеката androidx.security:security-crypto се добавя чрез Gradle.
Във файла build.gradle (app) добавете: implementation „androidx.security:security-crypto:1.1.0-alpha06“. За Kotlin проекти допълнително се изисква kotlin-stdlib. Инициализацията на MasterKey става еднократно, обикновено в Application.onCreate или чрез DI контейнер.
След създаване на инстанция на EncryptedSharedPreferences, API не се различава от обикновените SharedPreferences. edit() връща Editor, всички методи (putString, getString, putBoolean, getBoolean) работят аналогично. Разликата е само вътре: данните се криптират при запис и се декриптират при четене.
class AuthRepository(context: Context) {
private val prefs = createEncryptedPrefs(context)
fun saveCredentials(login: String, password: String) {
prefs.edit()
.putString("login", login)
.putString("password", password)
.apply()
}
fun getToken(): String? {
return prefs.getString("auth_token", null)
}
fun clearAll() {
prefs.edit().clear().apply()
}
}
За миграция на съществуващи данни от незащитени SharedPreferences към EncryptedSharedPreferences е необходимо: прочетете всички данни от стария файл, създайте нов EncryptedSharedPreferences, запишете всички данни, изтрийте стария файл. Google не предоставя вграден мигратор — разработчикът го имплементира самостоятелно.
Изборът между SharedPreferences и EncryptedSharedPreferences зависи от типа на съхраняваните данни. За настройки на интерфейса (тема, език, сортиране) обикновените SharedPreferences са достатъчни. За поверителна информация (токени, пароли, ключове) EncryptedSharedPreferences е задължителен.
EncryptedSharedPreferences е по-бавен от обикновения поради криптографски операции. Записът на една текстова стойност отнема ~5-15 ms (зависи от размера на данните и хардуерното ускорение на AES). Четене — 2-5 ms. За повечето приложения това е незабележимо, но при пакетни операции (миграция, възстановяване) си струва да използвате apply() вместо commit().
Обикновените SharedPreferences не осигуряват никаква криптографска защита: XML файлът се чете от всеки процес с root достъп или чрез adb backup. EncryptedSharedPreferences криптира данните на ниво приложение, а главният ключ се съхранява в Android Keystore с възможност за хардуерна защита (StrongBox).
| Характеристика | SharedPreferences | EncryptedSharedPreferences |
|---|---|---|
| Съхранение | Отворен XML | Криптиран двоичен файл |
| Криптиране | Не | AES-256 GCM + SIV |
| Защита на ключове | Не | Android Keystore + StrongBox |
| Производителност | 0.1-1 ms | 2-15 ms |
| Препоръка | Настройки на UI | Токени, ключове, PII |
Използвайте EncryptedSharedPreferences за съхранение на: OAuth refresh token, API ключове за външни услуги, имейл или телефонен номер на потребителя, поверителни настройки на приложението (PIN, флагове за удостоверяване). За съхранение на биометрични данни или големи документи EncryptedSharedPreferences не е подходящ — използвайте EncryptedFile или Room с SQLCipher.
Общо правило: ако изтичането на данни ще навреди на потребителя или бизнеса — използвайте EncryptedSharedPreferences. Ако данните са само козметични (тема, език, сортиране) — обикновени SharedPreferences. EncryptedSharedPreferences има смисъл да се внедри веднага, без рефакторинг: замяната в съществуващ проект изисква миграция и обработка на стари некриптирани данни.
Не забравяйте, че EncryptedSharedPreferences защитава данните само на диска — не по време на работа на приложението. Ако нападателят има достъп до паметта на процеса, декриптираните данни могат да бъдат прихванати. Използвайте допълнителна защита: ProGuard/DexGuard за объркване на кода.
Често задавани въпроси
Jetpack DataStore — е по-модерна алтернатива на SharedPreferences, базирана на Flow и Kotlin корутини. DataStore не криптира данни по подразбиране, но може да бъде комбиниран с EncryptedSharedPreferences или използван с ръчно криптиране чрез Proto DataStore с криптографски протоколи.
Не се препоръчва. EncryptedSharedPreferences е предназначен за малки обеми (до 100-200 KB). За големи данни използвайте Room с SQLCipher или криптиране на файлове чрез EncryptedFile от същата библиотека AndroidX Security.
Не, автоматична миграция на схема не съществува. При промяна на структурата на данните разработчикът трябва ръчно да прочете старите данни чрез стария KeyGen и да ги запише чрез новия. Препоръчва се съхранение на версията на схемата в отделен параметър.
AndroidX Security 1.0.0 поддържа API 19+ (Android KitKat). Версия 1.1.0-alpha06 също поддържа API 19+. За StrongBox се изисква API 28+ и устройство с хардуерна поддръжка (Google Pixel 3+, Samsung Galaxy S9+).
Да, refresh token — е един от основните сценарии за използване. Криптиране AES-256 GCM, главен ключ в Keystore, биометрична защита — достатъчно ниво за OAuth токени. За access token с кратък живот също е подходящ, въпреки че някои екипи предпочитат да го съхраняват в паметта.
Заключение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също