MASTG-KNOW-0036: Shared Preferences
The SharedPreferences
API is commonly used to permanently save small collections of key-value pairs.
Since Android 4.2 (API level 17) the SharedPreferences
object can only be declared to be private (and not world-readable, i.e. accessible to all apps). However, since data stored in a SharedPreferences
object is written to a plain-text XML file so its misuse can often lead to exposure of sensitive data.
Consider the following example:
var sharedPref = getSharedPreferences("key", Context.MODE_PRIVATE)
var editor = sharedPref.edit()
editor.putString("username", "administrator")
editor.putString("password", "supersecret")
editor.commit()
Once the activity has been called, the file key.xml will be created with the provided data. This code violates several best practices.
- The username and password are stored in clear text in
/data/data/<package-name>/shared_prefs/key.xml
.
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="username">administrator</string>
<string name="password">supersecret</string>
</map>
MODE_PRIVATE
makes the file only accessible by the calling app. See "Use SharedPreferences in private mode".
Other insecure modes exist, such as
MODE_WORLD_READABLE
andMODE_WORLD_WRITEABLE
, but they have been deprecated since Android 4.2 (API level 17) and removed in Android 7.0 (API Level 24). Therefore, only apps running on an older OS version (android:minSdkVersion
less than 17) will be affected. Otherwise, Android will throw a SecurityException. If an app needs to share private files with other apps, it is best to use a FileProvider with the FLAG_GRANT_READ_URI_PERMISSION. See Sharing Files for more details.
You might also use EncryptedSharedPreferences
, which is wrapper of SharedPreferences
that automatically encrypts all data stored to the shared preferences.
Warning
The Jetpack security crypto library, including the EncryptedFile
and EncryptedSharedPreferences
classes, has been deprecated. However, since an official replacement has not yet been released, we recommend using these classes until one is available.
var masterKey: MasterKey? = null
masterKey = Builder(this)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val sharedPreferences: SharedPreferences = EncryptedSharedPreferences.create(
this,
"secret_shared_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val editor = sharedPreferences.edit()
editor.putString("username", "administrator")
editor.putString("password", "supersecret")
editor.commit()