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 користи двонivoску шему шифровања: главни кључ (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 за чување: refresh token OAuth, API кључева за спољне сервисе, email или телефонског броја корисника, поверљивих подешавања апликације (PIN, флагови аутентификације). За чување биометријских података или великих докумената EncryptedSharedPreferences није погодан — користите EncryptedFile или Room са SQLCipher.
Опште правило: ако цурење података наноси штету кориснику или послу — користите EncryptedSharedPreferences. Ако су подаци само козметички (тема, језик, сортирање) — обичне SharedPreferences. EncryptedSharedPreferences има смисла имплементирати одмах, без рефакторисања: замена у постојећем пројекту захтева миграцију и обраду старих нешифрованих података.
Запамтите да EncryptedSharedPreferences не штити податке током рада апликације — само на диску. Ако нападач има приступ меморији процеса, дешифровани подаци могу бити пресретнути. Користите додатну заштиту: ProGuard/DexGuard за обфускацију кода.
Често постављана питања
Jetpack DataStore — је модернија алтернатива SharedPreferences, заснована на Flow и Kotlin coroutines. 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође