EncryptedSharedPreference

EncryptedSharedPreference

2025년 8월 4일

SharedPreferences #

앱 데이터 경로의 /data/data/{package_name}/shared_prefs/{filename}.xml 에 Key-Value가 xml 형식의 파일로 저장되는 간단한 로컬 데이터 저장소이다.


사용법 #

쓰기 #

1SharedPreferences sharedPref = getSharedPreferences("score_prefs", Context.MODE_PRIVATE);
2
3SharedPreferences.Editor editor = sharedPref.edit();
4editor.putInt(getString(R.string.saved_high_score_key), newHighScore);
5editor.putString(getString(R.string.saved_name_key), userName);
6editor.putBoolean(getString(R.string.saved_istop_key), isTop);
7editor.apply();

결과 #

/data/data/{package_name}/shared_prefs/score_prefs.xml

1<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
2<map>
3    <int name="high_score" value="214748364800" />
4    <string name="user_name">dhkim@gmail.com</string>
5    <boolean name="is_top" value="true" />
6</map>

읽기 #

1SharedPreferences sharedPref = getSharedPreferences("score_prefs", Context.MODE_PRIVATE);
2
3val score = sharedPref.getInt(getString(R.string.saved_high_score_key), 0);
4val name = sharedPref.getString(getString(R.string.saved_name_key), "");
5val istop = sharedPref.getBoolean(getString(R.string.saved_istop_key), false);

EncryptedSharedPreferences #

SharedPreferences 를 Keystore 기반으로 암호화하여 루팅된 기기에서 직접 파일을 읽어 유출되는것이 방지된다.
동적으로 앱을 조작할 수 있는 Frida나 메모리 후킹에서는 취약할 수 있다.


사용법 #

키 생성 #

1MasterKey masterKey = new MasterKey.Builder(context)
2    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
3    .build();

쓰기 #

sharedPrefs 를 EncryptedSharedPreferences로 생성해서 가져오면 된다.

 1EncryptedSharedPreferences sharedPref = EncryptedSharedPreferences.create(
 2    context,
 3    "score_prefs",  // 암호화된 파일 이름
 4    masterKey,      // 위에서 생성한 키
 5    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
 6    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
 7);
 8
 9// 사용법은 같다. 
10SharedPreferences.Editor editor = sharedPref.edit();
11editor.putInt(context.getString(R.string.saved_high_score_key), newHighScore);
12editor.putString(context.getString(R.string.saved_name_key), userName);
13editor.putBoolean(context.getString(R.string.saved_istop_key), isTop);
14editor.apply();

결과 #

알 수 없는 아주 어지러운 결과가 표시된다.
/data/data/{package_name}/shared_prefs/score_prefs.xml

1<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
2<map>
3    <string name="AXfKRRG1W4U19wMCtF7/zHjF8/QmYzzZQ2DrahsEDA==">AX3m6IEgmC75ZIgynQIqbRDGTFB+Urb2biif8verVPb+vFQyrQDhFnU=</string>
4    <string name="AXfKRRFA6LrybBXcRj5J1h8MlgSztOeFGI1hmm3o">AX3m6IE6l5T2t9VEhVrUncNUOQrxrcncFUdqwCD/2+AVEFSLMT3ZUCmQjZhj6q6171tijVRH9NU=</string>
5    <string name="AXfKRREbFUKw4XyD0PgPjzwI3eglfnfmI9yc">AX3m6IHyqFwrg+aUmQRaFsfYt7DTDM5SzztivZW2BMIaS07ss0o=</string>
6    <string name="__androidx_security_crypto_encrypted_prefs_key_keyset__">12a901804a802908fbc0fb51c3e6f862be8ddb2e961af7abd88245febf914c7306917c546b4f1c3dbf64f4799972a192ae095a6f16dd80cffdce57959abf06e22b5bbad4afd6d6e3167d46f2a7cd5600e3f0fc0b46ec4af841e7c965c437d6b26f87bac4fcdd9069cc4b687a225cc7b7fbc63ecfd7ee65f64163ead4ef2300cf5e968cf0c83b995296c778b0c269f6b96eb35880a06aacb232f7362fcca87a6d90f35a3e975f438b04ec98211a4408918aa9be07123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b6579100118918aa9be072001</string>
7    <string name="__androidx_security_crypto_encrypted_prefs_value_keyset__">1288018eb9419856bc19c30b92c7751619fd3f3209b65c9b024caba1e1ccd929cd94c9ff2de95275ad388452c2f57cdcac670b62869f2030a97855c14cc69c0b71b66aa65ef8d959879e2615bfd19f081569c8c203518f2d4bfd599db16d5dc34b05f58fa8104bdd0b0f9f8e36bbf912fae5b4eb38e405ee9a2e1bade8ee4ce5dbade4922e2781a83aee621a440881d19bef07123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b657910011881d19bef072001</string>
8</map>

이 파일을 보면 key_keyset과 value_keyset이 보인다.

SharedPreferences 의 Key와 Value를 각각 암호화하는 키를 Keystore로 얻은 MasterKey를 이용해서 암호화한 값을 메타데이터와 함께 저장한것이다.
읽어올땐 이 값들에서 키를 꺼내 MasterKey로 복호화하고 각각 xml파일에서 Key와 Value를 복호화하여 데이터를 복원한다.

hex로 읽어보면 EncryptedSharedPreferences.create 호출 때 사용한 알고리즘을 확인할 수 있다.

9ee9c0ae-602b-417d-9e3b-0bf3e8df1541


읽기 #

 1EncryptedSharedPreferences sharedPref = EncryptedSharedPreferences.create(
 2    context,
 3    "score_prefs",
 4    masterKey,
 5    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
 6    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
 7);
 8
 9int score = sharedPref.getInt(context.getString(R.string.saved_high_score_key), 0);
10String name = sharedPref.getString(context.getString(R.string.saved_name_key), "");
11boolean isTop = sharedPref.getBoolean(context.getString(R.string.saved_istop_key), false);

런타임에 복호화하기 #

Keystore를 사용한 로직이기 때문에 런타임에 Frida로 붙어서 복호화해야한다.

comments powered by Disqus