Skip to main content
Start your own AI-powered blog — freeGet started →

Android App Security: Protecting User Data on the Play Store

Podcast episode2 voices
6:27
Android App Security: Protecting User Data on the Play Store
Photo by Denny Müller on unsplash

The practical security checklist I run on every Android app I touch — from the Play Data Safety form to encrypted storage, with the code and the failure modes.

Last year I was asked to review the security posture of an existing Android app before a funding round. The app had 300,000 installs and stored users' payment tokenization references, addresses, and order history. The audit took me a weekend and it was not encouraging.

The app had three problems that together would have made any competent attacker smile: it sent API calls over plain HTTP in two places, it stored a refresh token in plain SharedPreferences, and it logged the full request body — including a bearer token — to Logcat in debug builds that had been shipped to production with minifyEnabled off. None of these were exotic zero-days. They were everyday mistakes. And the Play Data Safety form the team had filed claimed the opposite of what the code did.

Here is the thing about Android security: it is not one big feature you bolt on at the end. It is a set of small, boring, repeatable decisions made in every screen you touch. This article is the exact checklist I walk through — in the order I walk through it — with the code that actually works and the pitfalls that make each layer fail.

Step 1 — File the Play Data Safety Form Truthfully

The Play Data Safety form is not an admin chore; it is a legal statement about your app's behavior, and Google has been enforcing it since 2022. If you say "no data collected" and the app sends analytics, your app gets flagged, suspended, or pulled.

Before you touch any code, enumerate what your app actually does:

  • Which data types you collect (location, contacts, emails, financial info, device IDs).
  • Where the data goes (on-device only, or to your servers).
  • Whether you encrypt it in transit and at rest.
  • Whether it is shared with third parties (AdMob, Firebase, crash SDKs).

Every SDK you add — ad networks, analytics, crash reporters — adds data collection you now have to disclose. I keep a PRIVACY.md in every Android repo that lists each SDK, what it collects, and where it sends it. When a new SDK lands in a PR, the PR description must update that file. It turns a scary form into a paper trail you already wrote.

Step 2 — Force HTTPS Everywhere (and Prove It)

Plain HTTP is not a corner case to be tolerated; it is the top of the kill list. On modern Android, cleartext traffic is blocked by default from API 28 (Android 9) onward, but android:usesCleartextTraffic="true" or a permissive network security config silently re-enables it — and I find both in production apps all the time.

The correct move is a network security config that forbids cleartext and pins where you need it:

xml
<!-- res/xml/network_security_config.xml -->
<network-security-config>
    <base-config cleartextTrafficPermitted="false" />
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.example.com</domain>
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </domain-config>
</network-security-config>

Then reference it in the manifest and add the Android 7+ default:

xml
<application
    android:networkSecurityConfig="@xml/network_security_config"
    android:usesCleartextTraffic="false">

The cleartext flag is ignored when a network security config is present, so keeping both consistent matters. After this, run the app against a proxy like Burp Suite or mitmproxy and watch for any HTTP request. If one appears, you have a developer who hardcoded a URL somewhere — fix it, do not debate it.

About certificate pinning: it is a strong control, but it is also the one that bricks your app when your certificate rotates and you shipped a hardcoded pin. If you pin, pin a backup set, build pin rotation into your release process, and be ready to ship a hotfix. For most apps, HTTPS with a proper config is enough; pinning is for high-value targets like banking or government apps.

Step 3 — Do Not Trust the Client with Secrets

The single most common leak I find is an API key, client secret, or admin token compiled into the app. I cannot say this loudly enough: anything in your APK can be extracted. A simple strings or a dex decompiler like jadx will find a hardcoded key in minutes, and the Google Play scraping community automates exactly that.

The rule is simple: if a secret is not meant for the user to see, it does not belong in the client at all.

  • Move third-party keys that must stay secret to your backend, and proxy the calls.
  • For analytics and crash SDKs whose keys are designed to be public (Firebase Web API keys, for example), still restrict the domain and package in the console, and do not use those keys for anything privileged.
  • Never, ever put a backend admin token or a database password in the app. I have seen a real production app ship with a postgres:// connection string in its source. Do not be that team.

If you are tempted by BuildConfig fields, remember: BuildConfig is compiled into the APK and is trivially readable. It stops casual snooping and nothing else. Treat it as a placeholder, not a vault.

Step 4 — Encrypt Data at Rest

SharedPreferences and plain files are readable on a rooted device, and worse, they get picked up by backup mechanisms and by careless file exports. Anything sensitive — tokens, user details, offline data — belongs in encrypted storage.

The Jetpack Security library wraps the Android Keystore and gives you EncryptedSharedPreferences and EncryptedFile without you handling keys:

kotlin
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val prefs = EncryptedSharedPreferences.create(
    context,
    "secure_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

prefs.edit().putString("refresh_token", token).apply()

Every read and write now happens encrypted at the API level; the KeyStore holds the master key outside the app's reach. The two mistakes I see here are wrapping the wrong things (encrypting everything including non-sensitive UI state, which just slows you down) and forgetting that the encrypted prefs themselves still need the right access rules — if the device has no lock screen, the keystore can be weaker. For genuinely sensitive data, require a device lock and consider user-authentication-required keys:

kotlin
KeyGenParameterSpec.Builder(
    "user_auth_key",
    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(60)
.build()

Step 5 — Store Only What You Must

Encryption is not a magic shield; if your app holds sensitive data it does not need, encryption just protects a mistake. This is where I do the hard pruning in every audit:

  • Do not store the raw password or full credit card number. Store tokens, references, or vaulted data behind your payment provider.
  • Do not keep a refresh token forever. Store it with an expiry, rotate it, and revoke it on logout and on account compromise.
  • Do not cache screenshots or clipboard contents. If you build an app that copies sensitive values to the clipboard, clear the clipboard after a timeout, and disable screenshots in sensitive screens with FLAG_SECURE.
  • Keep tokens in memory, not on disk. If the app can survive a process restart without the token, do not persist it at all.

Step 6 — Secure the Backup Trail

This is the sneakiest leak. Android auto-backup copies SharedPreferences, databases, and files to Google Drive by default — including, in the past, data from encrypted prefs when the keys could not follow. A restored backup on a new device can re-materialize sensitive data or, worse, a stale session.

In the manifest, disable backup or exclude the sensitive bits:

xml
<application
    android:allowBackup="false">

Or keep backup on but exclude what matters:

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

<!-- res/xml/backup_rules.xml -->
<full-backup-content>
    <exclude domain="sharedpref" path="secure_prefs.xml" />
    <exclude domain="database" path="session.db" />
</full-backup-content>

If you use auto-backup, the Data Extraction Rules XML (android:dataExtractionRules) is the modern replacement on Android 12+ — apply both to cover old and new devices. I default to allowBackup="false" for apps that hold financial or health data, and only re-enable it with explicit exclusions when the product genuinely needs it.

Step 7 — Enforce Least-Privilege Permissions

Every runtime permission you request is an attack surface. I audit the manifest for the classic sins: READ_CONTACTS requested by a calculator app, ACCESS_FINE_LOCATION requested always instead of foreground-only, and the bloatware habit of requesting permissions "for future features."

  • Request permissions at the moment of need, with an explanation of why.
  • Prefer ACCESS_COARSE_LOCATION unless fine location is genuinely required.
  • Re-request is fine, but nagging dialogs get apps removed from consideration by users — and Google ranks unrequested permissions against your declared need.
  • Review the permission list on every release. If a permission no longer has a code path, delete it.
kotlin
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
    != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQ_CAMERA)
}

Step 8 — Obfuscate and Minify, and Mean It

R8 obfuscation is not security, but it is the fence that makes casual extraction take longer than a lazy attacker is willing to spend. It also shrinks your APK. Too many teams ship with minifyEnabled false because a library broke once under obfuscation and they never went back.

kotlin
// build.gradle.kts (app module)
buildTypes {
    release {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
    }
}

Expect a fight with reflection-heavy libraries (Gson, Retrofit models, some SDKs) — that is what keep rules are for. Spend the day getting R8 to pass with your real rules instead of disabling it. And critically: R8 does not protect strings. That hardcoded API key is still there, just renamed. Step 3 still rules.

Step 9 — Stop the Logs

Debug logs that leak tokens, PII, or request bodies are a free data breach for anyone with adb access or a decompiled release build that still logs. The fix is systemic, not a hope:

  • Do not log full request bodies or auth headers anywhere, ever.
  • Gate all your logging behind a build type check so release builds log nothing sensitive:
kotlin
if (BuildConfig.DEBUG) {
    Log.d("App", "onCreate called")
}
  • Use Timber with a release tree that drops everything except critical error events, and even those go through a redactor that strips emails, tokens, and phone numbers before the crash SDK sees them.

The app I audited logged the refresh token on every login. The fix took one day. The exposure had been running for two years.

Step 10 — Handle the Signing Keys Like Treasure

Your upload key signs your app; whoever holds it can push updates to every one of your users' devices. A leaked upload key is effectively a permanent backdoor. The hygiene is boring and non-negotiable:

  • Keep the upload key offline or in a hardware vault, not in the repo, not in CI logs.
  • Use Play App Signing so the key that actually signs for distribution is managed by Google, and you keep only the upload key.
  • Rotate keys on a schedule and on any suspected exposure.
  • Never commit keystore files. A .gitignore entry for *.jks and *.keystore is not optional.

The Common Pitfalls, Compressed

  • usesCleartextTraffic="true" shipped "temporarily" and never removed.
  • Hardcoded keys because "the backend is not ready."
  • EncryptedSharedPreferences used, but the master key stored in plain prefs — the most ironic way to fail.
  • Backup left on, silently copying session data to the cloud.
  • R8 disabled because one SDK broke, and nobody ever revisited.
  • allowBackup true plus the Keystore-based key unexportable, producing a crash on restore — the failure mode that makes teams disable backup rather than fix the exclusion rules.
  • Logging PII because "it's just a debug build" and the debug build going to production.

The Release-Gate Checklist

Before I push a release to the Play Console, this list must all be checked:

  • Play Data Safety form matches what the code actually does.
  • No cleartext traffic; network security config forbids HTTP; proxied test passed.
  • No secrets in the APK; backend holds anything privileged.
  • Tokens and sensitive data in EncryptedSharedPreferences or memory only.
  • Permissions minimized; no unused permissions in the manifest.
  • R8 enabled with working keep rules; resources shrunk.
  • No sensitive logging in release; logs redacted.
  • Backup disabled or explicitly excluding sensitive data.
  • Keystore file not in the repo; upload key rotation on schedule.
  • Third-party SDKs reviewed: what they collect, where it goes, and whether you declared it.

The funding-round app I audited shipped the fixes a month later: encrypted prefs, HTTPS everywhere, R8 on, logging redacted, backup tightened, and a truthful Data Safety form. It also stopped being a story I tell investors with a wince.

Android security is not exotic. It is a checklist, executed honestly, every release. Most data breaches in Android apps are not sophisticated attacks — they are a token sitting in plain prefs and an API call over HTTP. Close those holes first, and you have closed 80 percent of the practical risk before you ever worry about the attacker with the fancy exploit.


*Gulshan Yad

Securing Android Device Boot Process

The Android device boot process is a critical component of overall security. To ensure a secure boot process, verify that your device is using a trusted and up-to-date bootloader. This can be done by checking the device's settings or consulting the manufacturer's documentation.

Additionally, ensure that your device is using a secure lock screen, such as a fingerprint or facial recognition, to prevent unauthorized access. This will also help protect your device from malware that may attempt to bypass a weak password.

Implementing Secure Data Storage

Secure data storage is essential for protecting sensitive information on your Android device. To achieve this, ensure that your device is using a reputable and secure data storage solution, such as a secure container or encrypted file system.

When choosing a secure data storage solution, consider the following factors:

  • Data encryption: Ensure that the solution provides robust data encryption to protect sensitive information from unauthorized access.
  • Access controls: Implement strict access controls to prevent unauthorized access to sensitive data.
  • Regular updates: Choose a solution that provides regular updates to ensure you have the latest security patches and features.
  • Compatibility: Select a solution that is compatible with your device and operating system.

Android App Security Best Practices

To ensure the security of your Android apps, follow these best practices:

  • Regularly update your apps: Ensure that your apps are up-to-date with the latest security patches and features.
  • Use strong passwords: Create unique, complex passwords for each app to prevent unauthorized access.
  • Enable two-factor authentication: Add an extra layer of security by enabling two-factor authentication for your apps.
  • Monitor app permissions: Regularly review your app permissions to ensure they align with your intended use.
  • Use a reputable antivirus app: Install a reputable antivirus app to scan for malware and other threats.
  • Be cautious when interacting with apps: Avoid clicking on suspicious links or downloading attachments from unknown sources.

Using Secure Communication Protocols

To ensure secure communication between your Android device and apps, use secure communication protocols such as HTTPS and TLS. These protocols provide end-to-end encryption, ensuring that data is protected from interception and eavesdropping.

Android Device Management

Android device management is critical for securing your device and protecting sensitive information. To manage your device securely, follow these steps:

  • Enable device encryption: Encrypt your device to protect sensitive information from unauthorized access.
  • Set up a lock screen: Implement a secure lock screen, such as a fingerprint or facial recognition, to prevent unauthorized access.
  • Regularly update your device: Ensure that your device is up-to-date with the latest security patches and features.
  • Use a reputable antivirus app: Install a reputable antivirus app to scan for malware and other threats.
  • Monitor your device's performance: Regularly check your device's performance to detect any potential security issues.

Android App Development Security

As an Android app developer, it is essential to prioritize security in your development process. To achieve this, follow these best practices:

  • Use secure coding practices: Implement secure coding practices to prevent vulnerabilities in your app.
  • Regularly update your app: Ensure that your app is up-to-date with the latest security patches and features.
  • Use secure data storage: Implement secure data storage solutions to protect sensitive information.
  • Monitor app permissions: Regularly review your app permissions to ensure they align with your intended use.
  • Use a reputable antivirus app: Install a reputable antivirus app to scan for malware and other threats.
  • Be cautious when interacting with users: Avoid collecting sensitive information and ensure that you handle user data securely.

Key Takeaways

  • Always verify the app's permissions before installing, ensuring they align with the app's functionality.
  • Regularly update your Android device and apps to ensure you have the latest security patches.
  • Use a reputable antivirus app to scan for malware and other threats.
  • Be cautious when clicking on links or downloading attachments from unknown sources.
  • Use a password manager to generate and store unique, complex passwords for each app.
  • Monitor your app permissions and revoke any unnecessary access to sensitive data.

Frequently Asked Questions

What are the most common security threats to Android apps on the Play Store?

Common security threats include malware, phishing, and unauthorized access to sensitive data. Be cautious when interacting with apps, especially those that request sensitive permissions or ask for payment information.

How do I verify an app's permissions on the Play Store?

On the Play Store, navigate to the app's page, click on the 'Details' tab, and scroll down to the 'Permissions' section. Ensure the permissions requested align with the app's intended functionality.

What happens if I download a malicious app from the Play Store?

If you download a malicious app, it may install malware, steal sensitive data, or disrupt your device's performance. Immediately uninstall the app and run a virus scan to detect and remove any threats.

Can I trust apps with high ratings and reviews on the Play Store?

While high ratings and reviews can indicate a legitimate app, they do not guarantee its security. Always verify an app's permissions and reviews from multiple sources before installing.

How often should I update my Android device and apps?

Regularly update your Android device and apps to ensure you have the latest security patches and features. Enable automatic updates for your device and apps to stay protected.

What are some best practices for creating strong passwords?

Use a password manager to generate unique, complex passwords for each app. Avoid using easily guessable information, such as your name or birthdate, and change your passwords regularly.

Can I use the same password for multiple apps?

No, using the same password for multiple apps is a security risk. Create unique passwords for each app to prevent unauthorized access to sensitive data.

What should I do if I suspect an app is stealing my data?

Immediately uninstall the app, change your passwords, and run a virus scan to detect and remove any malware. Report the issue to the Play Store and relevant authorities.

How can I protect my personal data on the Play Store?

Use a reputable antivirus app, enable two-factor authentication, and regularly review your app permissions to ensure they align with your intended use.

G
Gulshan Yadav

1 followers

AI systems builder · 7 years in production. RAG, self-hosted infra, agent architecture. 📬 Deep-dives → mrgulshanyadav.substack.com

Comments

Sign in to join the conversation

No comments yet. Be the first to share your thoughts!

More from Gulshan Yadav

Recommended for you