당신의 안드로이드 앱 데이터는 안전한가요?

🔐 데이터를 저장할 땐 반드시 암호화가 필요합니다.

security

KISA의 개인정보 안전성 확보조치 기준에 따르면
주민번호나 비밀번호와 같은 개인 정보를 모바일 단말에 저장할 때는 반드시 모두 암호화 해야합니다.

만약 보안에 민감한 서비스라서 모바일 보안 취약점 검사를 받는다면
위에 서술된 민감한 정보 뿐 아니라 전화번호, 생년월일, 이름 등을 포함한
사실상 거의 모든 정보를 암호화 해야합니다.

따라서 단말에 저장하는 데이터는 전부 암호화하는 것이 정신건강에 이롭습니다.
애초부터 암호화 하는 습관을 들이면 더더욱 좋구요 👍

👮‍♂️ EncryptedSharedPreferences

jetpack

Android Jetpack에 포함된 EncryptedSharedPreferences를 이용하면
복잡한 구현 없이도 손쉽게 SharedPreferences 데이터를 암호화할 수 있습니다.
갓드로이드킹트팩

비록 알파버전이나 사실상 구글 공식 라이브러리라서 안심하고 사용할 수 있고
기존의 SharedPreferences를 상속받고 있어서 처음 호출하는 부분만 수정하면
이후에 데이터를 읽고 쓰는 과정에서의 수정은 아예 없기 떄문에 무척 편리합니다.

실제로 사용해보면 키값과 데이터가 모두 암호화 되는 것을 확인하실 수 있습니다.

그럼 한번 직접 적용해볼까요?



1. build.gradle(app)에 의존성 추가

dependencies {
    implementation 'androidx.security:security-crypto:1.0.0-alpha02'
}

app 수준의 build.gradle 의 dependencies에 추가해주면 됩니다.

2. 코드상에서 EncryptedSharedPreferences를 호출하기

  val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)

  val sharedPreferences = EncryptedSharedPreferences.create(
      "secret_shared_prefs", // 파일이름을 입력하세요
      masterKeyAlias,
      context,
      EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
      EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
  )

  val editor: SharedPreferences.Editor = sharedPreferences.edit()

EncryptedSharedPreferences가 이미 SharedPreferences 이므로 이후에는
기존에 쉐어드프리퍼런스를 사용하던 곳에서 데이터를 읽고 쓰는 코드는 동일하므로
별도의 코드 수정 없이 기존에 사용하시던 대로 사용하면 됩니다.


🤔 그런데 말입니다

EncryptedSharedPreferences를 적용하고나서 저장한 모든 데이터는 암호화 되겠지만
이미 평문으로 저장된 데이터를 암호화 해주지는 않습니다 😭

그리고 안타깝게도 보통 이 글을 읽고 계신분이라면
이미 배포되어 사용자 단말에 저장된 데이터의 암호화가 필요하신 분들이 많으실 것 같습니다.

이미 배포된 앱이라서 사용자가 데이터를 평문으로 저장하고 있다면
저장된 데이터를 전부 암호화 하는 이관작업이 반드시 필요합니다.


여러가지 방법이 있을 수 있겠지만 이 글에서는 가장 간단한

  1. 기존 데이터를 삭제하고
  2. 새로운 파일에 암호화 해서 복사

하는 방법을 소개드리겠습니다.


📨 기존 데이터 이관하기

기존에 이미 사용하고 있는 데이터를 암호화 하는 문제는 또 별개의 문제입니다

가장 간단한 방법은 기존의 데이터를 전부 암호화하여 복사하고 기존의 데이터를 삭제하여
무조건 암호화 된 데이터만 접근하도록 하는 방식입니다.

⚠️ 복사하고 기존 데이터를 삭제하는 방식이므로 암호화 할 파일이름과 기존 파일이름은 달라야 합니다.

// 암호화 되지 않은 레거시 데이터
val legacy = SharedPreferences by mContext.getSharedPreferences(mFile, Context.MODE_PRIVATE)
val entries = legacy.all.entries

// 모든 데이터를 복사한다.
// editor는 EncryptedSharedPreferences의 editor 
entries.forEach {
    when (it.value) {
        is String -> editor.putString(it.key, it.value as String).apply()
        is Long -> editor.putLong(it.key, it.value as Long).apply()
        is Int -> editor.putInt(it.key, it.value as Int).apply()
        is Float -> editor.putFloat(it.key, it.value as Float).apply()
        is Boolean -> editor.putBoolean(it.key, it.value as Boolean).apply()
        is Set<*> -> editor.putStringSet(it.key, it.value as Set<String>).apply()
    }
}

복사를 다했다면 기존의 평문 데이터를 전부 지워버립시다.

legacy.edit().clear().apply()




그런데 잠깐, 최소지원이 api23 이라구요? 😱 😱 😱

no_please_no

네 그렇습니다.
마시멜로이하는 이 방법으로는 할 수 가 없기 때문에
결국 새로운 암호화 모듈을 만들어서 구현해야 합니다.😱

문제는 아직까지도 많은 앱들의 23미만의 앱을 지원하고 있기 때문에
기껏 EncryptedSharedPreferences가 있어도 활용하기가 어렵다는 점이 가장 큰 문제입니다.

🤷‍♂️ 그럼 이제 어떡하죠?

아직 방법이 있습니다.

BrickSharedPreferences를 사용해보세요!

BrickSharedPreferences가 뭔데?

BrickSharedPreferences


Api 레벨 상관 없이 SharedPreferences를 암호화해주는 라이브러리 입니다.

minSdk 14로 낮은 레벨까지 지원하며
23 이상이라면 제트팩의 EncryptedSharedPreferences를 사용하고
23 미만이라면 커스텀 암호화 SharedPreferences를 이용하는 보안 라이브러리 입니다.

또한 추가로 위에서 설명한 ‘기존 데이터 이관’을 단 한줄로 해결할 수 있습니다.

1. build.gradle(app)에 의존성 추가

dependencies {
    implementation 'dev.haenara:bricksharedpref:1.0.2'
    implementation 'androidx.security:security-crypto:1.0.0-alpha02'
}

2. 코드상에서 BrickSharedPreferences를 호출하기

// 기존 SharedPreferences를 가져오는 방법에 "Brick"만 앞에 붙이면 된다. 
val mSharedPreferences = getBrickSharedPreferences(fileName, Context.MODE_PRIVATE)

기존에 쉐어드프리퍼런스 호출 시에 코틀린에서 사용하던 코드와 거의 유사한데,
getSharedPreferences
getBrickSharedPreferences로 고친 것이 전부입니다.

BrickSharedPreferences도 기존에 쉐어드프리퍼런스를 사용하던 곳에서 데이터를 읽고 쓰는 코드는 동일하므로
별도의 코드 수정 없이 기존에 사용하시던 대로 사용하면 됩니다.


3. 기존 데이터 이관하기

단 한줄이면 됩니다.

// 저장되어 있는 데이터를 전부 암호화
mSharedPreferences.migrateEncryptedSharedPreferences()

요거 한줄이면 해결됩니다.

sample_run
sample_run
sample_run
sample_run


단, 만약 SharedPreferences를 여러 파일로 나누어 사용중이라면
각각 파일별로 마이그레이션을 호출해줘야 합니다.

이관 작업은 앱 실행 시 딱 한번만 호출해주면 되기 때문에
Application class의 onCreate() 에서 호출해주는 것이 가장 좋습니다.



4. 실행 예제

sample_run

BrickSharedPreferences 레포지토리에 자세하게 설명되어 있으니

필요하시다면 샘플 앱과 코드를 참고해보시는 것도 좋을 것 같습니다👍




🚀 결론

모바일 단말에 저장하는 모든 데이터는 암호화 해야합니다.
BrickSharedPreferences를 이용해서 간편하게 데이터를 암호화 해보세요.
만약 유용하게 쓰셨다면 깃헙에서 ⭐️스타를 눌러주세요 👍

레퍼런스