Skip to content
All posts
March 4, 20264 min read

Security Testing for Android Apps: What Every Developer Should Check

Security vulnerabilities in Android apps can expose user data, bypass authentication, and get your app removed from the Play Store. Here's a practical security testing checklist developers can run themselves.

AndroidSecurityTestingKotlin
Share:

You don't need a dedicated security team to catch the most common Android vulnerabilities. Most security issues follow predictable patterns that you can find with the right checklist.

Here's what to check before every release.


Data Storage

The most common category of Android vulnerabilities: storing sensitive data where it shouldn't be.

SharedPreferences

SharedPreferences are stored in plain text on the device. Never store passwords, tokens, or PII here.

kotlin
// BAD — user token in SharedPreferences
prefs.edit().putString("auth_token", userToken).apply()

// GOOD — use EncryptedSharedPreferences
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

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

Internal vs External Storage

Files in external storage (Downloads, SDCard) are accessible to other apps with READ_EXTERNAL_STORAGE permission. Sensitive files belong in internal storage:

kotlin
// Internal storage — app private
val file = File(context.filesDir, "user_data.json")

Logging

Logs in release builds are a data leak. Search your codebase for:

bash
grep -r "Log\.d\|Log\.v\|Log\.i\|println" --include="*.kt" app/src/main/

Any log statement that includes tokens, passwords, PII, or API keys is a vulnerability. Use ProGuard to strip logs in release:

code
-assumenosideeffects class android.util.Log {
    public static *** v(...);
    public static *** d(...);
    public static *** i(...);
}

Network Security

Certificate Pinning

SSL/TLS prevents eavesdropping, but a MITM attack with a trusted CA can still intercept traffic. Certificate pinning ensures your app only accepts your server's specific certificate.

kotlin
val certificatePinner = CertificatePinner.Builder()
    .add("api.yourapp.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    .build()

val client = OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build()

Network Security Config

Prevent cleartext (HTTP) traffic in production:

xml
<!-- res/xml/network_security_config.xml -->
<network-security-config>
    <base-config cleartextTrafficPermitted="false" />
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" /> <!-- allows Charles/Fiddler in debug -->
        </trust-anchors>
    </debug-overrides>
</network-security-config>
xml
<!-- AndroidManifest.xml -->
<application
    android:networkSecurityConfig="@xml/network_security_config">

Authentication and Authorization

Token Storage

Auth tokens don't belong in SharedPreferences (see above). Use the Android Keystore system or EncryptedSharedPreferences.

Token Expiry

Verify that:

  1. Expired tokens are rejected by the backend
  2. The app handles 401 responses by clearing the token and redirecting to login — not silently swallowing the error

Biometric Authentication

If using biometric auth, verify it's tied to a Keystore key — not just a boolean flag:

kotlin
// WRONG — biometric just unlocks a boolean
if (biometricPassed) {
    showSecureContent()
}

// RIGHT — biometric unlocks a key that decrypts the data
val cipher = getCipherForDecryption() // tied to Keystore key with biometric binding
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))

The Manifest

Exported Components

Every Activity, Service, BroadcastReceiver, and ContentProvider with

code
android:exported="true"
is accessible to other apps.

xml
<!-- Only export components that need to be external -->
<activity android:name=".MainActivity" android:exported="true" />

<!-- Internal activities should NOT be exported -->
<activity android:name=".InternalSettingsActivity" android:exported="false" />

Audit your manifest. Every exported component should have a justification.

Permissions

Review requested permissions:

  • Does the app actually need CAMERA? MICROPHONE? LOCATION?
  • Are permissions requested at the right time (contextually)?
  • Are dangerous permissions handled with graceful degradation if denied?

Remove any permission your app doesn't actually use.


Testing With MobSF

Mobile Security Framework (MobSF) performs automated static analysis on your APK:

bash
# Run MobSF via Docker
docker run -it --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest

# Open http://localhost:8000
# Upload your APK
# Get a comprehensive security report

MobSF catches:

  • Insecure data storage
  • Weak cryptography
  • Exported components without proper protection
  • Hardcoded credentials and API keys
  • Insecure network configurations

Run this before every major release.


Quick Manual Checks

bash
# Check for hardcoded secrets in code
grep -r "password\|api_key\|secret\|token" --include="*.kt" app/src/main/ -i

# Check for http:// URLs (should be https://)
grep -r "http://" --include="*.kt" --include="*.xml" app/src/

# Check for overly broad file permissions
grep -r "MODE_WORLD_READABLE\|MODE_WORLD_WRITEABLE" --include="*.kt" app/src/

Pre-Release Security Checklist

  • No sensitive data in SharedPreferences (use EncryptedSharedPreferences)
  • No logs in release builds that expose tokens/PII
  • All network traffic over HTTPS (networkSecurityConfig)
  • Exported components in manifest are intentional and protected
  • Auth tokens stored in EncryptedSharedPreferences or Keystore
  • App handles 401 responses by logging out (not silently continuing)
  • Certificate pinning for sensitive endpoints
  • No hardcoded API keys or credentials in source code
  • MobSF scan shows no critical findings

Takeaways

  • Most Android security vulnerabilities are preventable with standard patterns
  • Data storage is the most common vulnerability category — always encrypt sensitive data
  • Audit your manifest for unnecessary exported components
  • Run MobSF on your APK before major releases — it catches things code review misses
  • Security testing is part of the release checklist, not an afterthought
Share:
S

Sudarshan Chaudhari

AI Systems Builder / Product Engineer

Bangkok, Thailand

Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.

Stay updated

Get new posts on Android, Kotlin, and solo dev straight to your inbox.

Newsletter preferences

Related Apps

MyFamilyTracker

Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.

Building something? Available for Android dev and QA consulting.

Work with me

Comments — powered by Giscus

Apps tagged with this