티스토리 뷰

앱 개발 시에 고도화된 암복호화 방식을 사용해보기 위해,

기존 SharedPreferences를 사용하던 부분을

MasterKey 기반 보안 저장소인 EncryptedSharedPreferences로 교체했다.

그 과정에서 새롭게 알게된 부분과 겪은 이슈를 정리해보려고 한다.


MasterKey

  • AndroidX Security 라이브러리에서 EncryptedSharedPreferences 또는 EncryptedFile 사용 시 내부적으로 관리되는 AES 암호화용 루트 키
  • 앱 내 암복호화 시 공통적으로 사용되는 키를 안전하게 관리하는 객체
  • 실제 키는 Android Keystore(System-level secure storage)에 저장됨
  • 앱의 인증서/서명에 따라 관리되며, 앱 재설치 시 다른 키로 재생성됨
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

 

EncryptedSharedPreferences

  • 내부적으로 MasterKey를 사용해 AES 암호화를 적용하는 SharedPreferences 
  • 키(Key)는 AES256-SIV (deterministic encryption)
  • 값(Value)은 AES256-GCM (authenticated encryption)
  • 앱 재설치 시, MasterKey 교체로 인해 이전 데이터 복호화 불가
val sharedPrefs = EncryptedSharedPreferences.create(
    context,
    "secure_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

 

 

문제 상황

  • MasterKey는 앱의 서명 인증서(Signing Certificate)를 기반으로 생성, 관리된다.
    때문에 앱 서명이 달라지거나, 앱이 재설치되어 Keysotre 영역이 초기화되면 새로운 key가 생성된다.
  • EncryptedSharedPreferences는 기본적으로 앱 삭제 시 함께 초기화된다.
    그러나 AndroidManifest에 allowBackup=true 로 설정되어 있으면, SharedPreferences 파일이 자동 복원될 수 있다.
    이 경우 복호화에 필요한 MasterKey는 복원되지 않기 때문에, 복원된 데이터는 복호화 오류를 일으킨다.

내 경우, Room DB에 데이터를 암호화하여 저장할 때 사용한 secretKey를 EncryptedSharedPreferences에 저장했다.
이 때 앱에서 allowBackup을 true로 설정해놓은 것을 놓쳐, 복원된 EncryptedSharedPreferences이 복호화가 불가능한 문제가 발생했다. 고려할 수 있는 해결 방안은 아래와 같았다.

 

해결 방법

방안 1. auto backup 허용하되, 특정 SharedPreferences만 제외

allowBackup="true" 설정 자체를 없애는 대신, 아래와 같이 backup_rules.xml 파일을 정의하여 문제가 되는 EncryptedSharedPreferences 파일을 백업에서 제외할 수 있다. 

// AndroidManifest.xml

<application
    android:allowBackup="true"
    android:fullBackupContent="@xml/backup_rules" />
// res/xml/backup_rules.xml

<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
    <!-- 기본적으로 모든 데이터를 백업 -->
    <include domain="sharedpref" path="." />
    <!-- 제외 대상 -->
    <exclude domain="sharedpref" path="secure_prefs.xml" />
</full-backup-content>

 

직접 manifest 파일을 제어해 allowBackup 설정 자체를 건드릴 수 없어 고려한 방식이지만, 이 방법 역시 manifest에 해당 파일을 선언해줘야했다.

 

방안 2. 복호화 실패 시 복구 로직 추가

기존 코드에서 문제가 되어 Exception이 발생할 때, secretKey를 저장해놓은 EncryptedSharedPreferences 파일 자체를 삭제하는 방법도 있다. runtime에서 처리할 수 있다는 장점이 있어 방어코드로 처리할만한 로직이다.

try {
    val prefs = EncryptedSharedPreferences.create(...)
    prefs.getString("sdk_secret_key", null)
} catch (e: BadPaddingException) {
    context.deleteSharedPreferences("secure_prefs")
}

 

방안 3. MasterKey 의존 제거 

최종적으로는, EncryptedSharedPreferences를 제거하고 직접 AES 암복호화를 적용했다.

secretKey는 일반 SharedPreferences에 Base64 형태로 저장했다.

보안성은 MasterKey 적용방식보다는 낮지만, 현재 문제가 된 기능은 안정성을 최우선으로 하기 때문에 앱 업데이트 또는 재설치에 안전하게 작동하는 것이 더 중요해 이 방법을 채택했다.

 


운영 안정성, 보안 수준, 코드 복잡도 등을 모두 신경쓰며

적정수준을 충족하는 타협점을 찾는 것에 많은 고민을 하게 된 경험이였다.

이번 기능은 안정성이 최우선이기 때문에 오히려 과도한 로직을 덜어내고 코드를 간소화해 방식을 채택했지만,

민감정보가 포함되어 보안성이 중요시 되는 기능에서 정보 탈취등의 문제로 이어질 수도 있으니

앱에서 보안을 챙기기 위한 방안에 대해서도 더 해봐야겠다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함