Android

    How to Fix Insecure Data Storage in Android

    An Android app storing a session token in cleartext in a SharedPreferences XML file, next to an encrypted, Keystore-backed alternative.

    To fix insecure data storage in Android, work in a clear order: do not store sensitive data on the device at all when you can keep it server-side, and for anything you must persist, encrypt it with a key held in the Android Keystore and write it to app-internal storage rather than external storage. Plain SharedPreferences saves values as cleartext, so it is the wrong place for tokens or personal data. EncryptedSharedPreferences used to be the drop-in encrypted replacement, but the Jetpack Security Crypto library that provides it is now deprecated, so new code should use a Keystore-backed approach such as Jetpack DataStore combined with Tink, or your own Keystore encryption. Then keep secrets out of logs, cache, and backups.

    Short answer

    Insecure data storage means sensitive data sits somewhere on the device that another app, a rooted or stolen phone, or a backup can read, and the fix is to store less and encrypt the rest. Per Android's security guidance, internal storage is private to your app by default and is the right place for sensitive files, while external storage is shared and should never hold secrets. For key-value data, plain SharedPreferences is cleartext; EncryptedSharedPreferences added Keystore-backed encryption but its library is now deprecated, so use DataStore with Tink or direct Keystore encryption instead. This maps to the storage requirements in OWASP MASVS.

    What insecure data storage means

    Insecure data storage is the class of bug where an app keeps sensitive information in a place or a form that is not protected against someone who can reach the device's files. OWASP ranks it among the top mobile risks because it is common and quiet: the app works perfectly, but a token, password, or piece of personal data is sitting in cleartext where it can be recovered. The threat is not only a thief holding a stolen phone; it is also a rooted device, a malicious app with the right access, a device backup, and forensic extraction.

    The reason it happens so often is that the convenient storage options are plaintext by default. Writing a value to SharedPreferences or a SQLite row takes one line and stores it readable, so the easy path and the insecure path are the same path. Fixing it means treating on-device storage as untrusted ground: assume anything you write could be read by someone who should not, and decide, for each piece of data, whether it belongs on the device at all and, if so, in what encrypted form.

    Where Android apps leak data

    Most on-device leaks come from a short list of predictable places, and knowing them tells you where to look. SharedPreferences stores its data as a plaintext XML file in your app's internal directory. A SQLite database stores rows in cleartext unless you encrypt it. External storage is shared across the device and is the wrong home for anything private. Application logs often capture tokens or personal data that were passed through them. The system cache and temporary files can retain sensitive content. And automatic backups can copy your files off the device.

    None of these is malicious on its own; each is just unprotected by default. The pattern is that data written for convenience ends up in a readable file, and the file outlives the moment it was needed, in a log, a cache, or a backup archive. So the first move in a fix is an inventory: list every place your app writes data, mark which entries are sensitive, and you will usually find secrets in one of these predictable locations rather than somewhere exotic.

    Shared Preferences: why plain ones are insecure

    Plain SharedPreferences is insecure for secrets because it stores values as cleartext, so anyone who can read your app's files can read the values. The data lives in an XML file inside your app's internal storage, which is private on a healthy device, but that privacy evaporates on a rooted device, through a backup that includes the file, or via any path that grants file access. A session token or password written to SharedPreferences is therefore recoverable without breaking any encryption, because there is none.

    This does not make SharedPreferences wrong for everything. It is fine for non-sensitive settings such as a theme choice or a last-opened tab, where disclosure causes no harm. The mistake is using it for tokens, credentials, keys, or personal data, because those are exactly the values an attacker wants and exactly the values it stores in the clear. So the rule is simple: keep non-sensitive preferences in SharedPreferences if you like, and move anything sensitive to an encrypted store.

    EncryptedSharedPreferences and its deprecation

    EncryptedSharedPreferences was the standard answer to that problem: it wrapped the same key-value API but encrypted both keys and values using a master key held in the Android Keystore, so the data on disk was ciphertext and the encryption key never lived in your code. For years it was the recommended drop-in fix for sensitive key-value storage, and it still functions where it is already in use.

    The important update is that the Jetpack Security Crypto library providing EncryptedSharedPreferences and EncryptedFile is now deprecated, so it is not the choice for new code. Google's current direction for local persistence is Jetpack DataStore, which is not encrypted on its own, combined with an encryption library such as Google Tink, with keys protected by the Android Keystore. The Keystore remains the anchor in every version of the answer: it holds the key in hardware-backed storage where your app can use it but cannot extract it. So for new work, encrypt with a Keystore-backed key through DataStore plus Tink or your own encryption, and reserve EncryptedSharedPreferences for code that already depends on it.

    The storage fix by data type

    Matching each kind of data to the right home and protection is the core of the fix. The table below maps it.

    Data typeStore it whereProtection
    Session token, credentialPrefer server-side or in-memory; else encrypted internal storageKeystore-backed encryption
    Personal data (PII)App-internal storage, encryptedKeystore-backed encryption
    Structured recordsEncrypted database in internal storageEncrypted DB plus Keystore key
    Non-sensitive settingsSharedPreferences or DataStoreNone needed
    Large shared mediaScoped or external storageNever store secrets here

    Read the top rows as the priority: the best protection for a secret is not storing it on the device, and encryption with a Keystore key is the fallback when you must.

    Practical steps to remediate

    Start by inventorying every place the app writes data and marking what is sensitive, then remove what you do not need, since data you never store cannot leak. For secrets you can avoid persisting, keep them server-side and fetch on demand, or hold short-lived values in memory only. For sensitive data that genuinely must live on the device, encrypt it with a key from the Android Keystore and write it to internal storage, using DataStore with Tink or an equivalent Keystore-backed approach rather than the deprecated library.

    Then close the secondary leaks. Move any sensitive files off external storage into internal storage. Strip tokens and personal data out of log statements, especially in release builds. Exclude sensitive files from automatic backup so a backup archive does not carry your secrets off the device. Clear temporary and cache copies of sensitive content when you are done with them. Each step targets one of the predictable leak locations, and together they turn on-device storage from readable to protected.

    How to test your own storage responsibly

    On your own app and a device or emulator you control, you can confirm what is exposed by looking at where your app writes. Using a debuggable build, inspect your app's internal directory, the shared_prefs XML files and the databases folder, and open them to see whether sensitive values are sitting in cleartext. Check whether anything sensitive is written to external storage or appears in logcat while you exercise the app. A static analysis tool such as MobSF will flag plaintext storage, world-readable files, and an enabled backup flag for you.

    Keep this to apps and devices you own or are authorized to assess, which is the boundary for responsible testing. The goal is to see your data the way someone with file access would, so you know which values need encrypting and which files need to move or be excluded from backup. If you can read a token or personal record in a preferences file or a database, that is the finding, and the fix is to encrypt it with a Keystore key or stop storing it.

    Remediation checklist

    Working through these steps closes the common storage gaps. The checklist below covers them.

    StepActionDone?
    Inventory storageList every place the app writes data[ ]
    Store lessRemove secrets you do not need on-device[ ]
    Encrypt what remainsUse a Keystore-backed key, not plain SharedPreferences[ ]
    Use internal storageKeep sensitive files off external storage[ ]
    Exclude from backupStop sensitive files copying into backups[ ]
    Clean logs and cacheStrip secrets from logs and temporary files[ ]

    The step most often skipped is excluding sensitive files from backup, because the data looks safe on the device while a backup quietly carries it off.

    Where a scan fits

    Once you have encrypted and relocated your sensitive data, checking the whole app for the same pattern is worthwhile, since plaintext storage rarely appears in only one place.

    A scanner like PTKD.com analyzes your build and flags issues such as sensitive values stored in cleartext, hardcoded keys, an enabled backup flag on sensitive data, and over-broad permissions by severity, mapped to OWASP MASVS. To be clear about the boundary: PTKD reports these findings for you to fix; it does not encrypt your data or move your files. It points at the insecure storage so you can protect each item.

    What to take away

    • Fix insecure data storage by storing less first: keep sensitive data server-side or in memory when you can, so there is nothing on the device to leak.
    • Plain SharedPreferences and unencrypted databases store cleartext, so they are the wrong home for tokens, credentials, or personal data.
    • EncryptedSharedPreferences added Keystore-backed encryption, but its Jetpack Security Crypto library is deprecated, so new code should use DataStore with Tink or direct Keystore encryption.
    • Keep sensitive files in internal storage, exclude them from automatic backup, and strip secrets from logs and cache.
    • Test by inspecting your own app's stored files, and a tool like PTKD.com helps flag plaintext storage across the app.
    • #android
    • #insecure data storage
    • #encryptedsharedpreferences
    • #android keystore
    • #owasp masvs

    Frequently asked questions

    How do I fix insecure data storage in Android?
    Store less and encrypt the rest. Keep sensitive data server-side or in memory when you can, so there is nothing on the device to leak. For data that must be stored, encrypt it with a key held in the Android Keystore and write it to app-internal storage, not external storage. Move secrets out of plain SharedPreferences and unencrypted databases, strip them from logs, and exclude sensitive files from automatic backup. This maps to the storage requirements in OWASP MASVS.
    Is SharedPreferences secure for storing tokens?
    No. Plain SharedPreferences stores its values as cleartext in an XML file in your app's internal directory, so anyone who can read your app's files, on a rooted device, through a backup, or via any file-access path, can read a token or password directly, because there is no encryption. SharedPreferences is fine for non-sensitive settings like a theme choice, but tokens, credentials, keys, and personal data should go into an encrypted, Keystore-backed store instead.
    Should I use EncryptedSharedPreferences?
    It still works where it is already in use, but it is not the choice for new code. EncryptedSharedPreferences encrypted keys and values with a master key in the Android Keystore, but the Jetpack Security Crypto library that provides it, along with EncryptedFile, is now deprecated. For new work, Google's direction is Jetpack DataStore, which is not encrypted on its own, combined with an encryption library such as Google Tink, with keys protected by the Android Keystore, or your own Keystore-backed encryption.
    What is the Android Keystore's role in secure storage?
    The Keystore holds your encryption keys in hardware-backed storage where your app can use them but cannot extract them, which is why it anchors every version of the secure-storage answer. Instead of putting a key in your code, where it could be recovered, you generate or store the key in the Keystore and use it to encrypt data before writing it to internal storage. Whether you use DataStore with Tink or another approach, the key stays in the Keystore.
    How do I test my app for insecure data storage?
    On your own app and a device or emulator you control, use a debuggable build to inspect your app's internal directory, especially the shared_prefs XML files and the databases folder, and open them to see whether sensitive values sit in cleartext. Check whether anything sensitive lands on external storage or appears in logcat. A static analysis tool such as MobSF flags plaintext storage, world-readable files, and an enabled backup flag. Keep testing to apps you own or are authorized to assess.
    Where does insecure data storage fit in OWASP?
    Insecure data storage is one of the top mobile risks in OWASP's mobile security work, and it maps to the storage requirements in the OWASP Mobile Application Security Verification Standard (MASVS), which call for sensitive data to be kept off the device where possible and encrypted when stored, and for it to be kept out of logs, backups, and other unintended locations. The OWASP testing guide describes checking where an app stores data and whether it is protected.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free