Mobile apps are increasingly the primary attack surface for data breaches and fraud. According to the IBM Cost of a Data Breach Report 2023, the average cost of a mobile-related breach is $4.45 million — a 15% increase over three years. According to a study by NowSecure, 83% of tested mobile apps had at least one security vulnerability that could expose user data. The OWASP Mobile Application Security Verification Standard (MASVS) identifies critical vulnerability categories across iOS and Android apps, with insecure data storage and improper authentication consistently ranking as the top issues. For QA engineers, mobile security testing requires a specialized toolkit: static analysis (MobSF), dynamic analysis (Frida, Burp Suite), and penetration testing techniques tailored for mobile platforms.
TL;DR: Mobile security testing follows the OWASP MSTG framework: test for insecure data storage (unencrypted SQLite, logs, Keychain misuse), improper authentication, insecure network communication (SSL pinning bypass, MITM), code vulnerabilities (reverse engineering protection), and platform-specific issues (inter-app communication, clipboard attacks).
Mobile Security Testing Fundamentals
Mobile app security testing validates data protection, secure communication, authentication, and resistance to reverse engineering. Following OWASP MASVS (Mobile Application Security Verification Standard) ensures comprehensive coverage across all security domains.
OWASP Mobile Top 10
The OWASP Mobile Security Testing Guide provides the industry-standard methodology for mobile penetration testing.
- Improper Platform Usage
- Insecure Data Storage
- Insecure Communication
- Insecure Authentication
- Insufficient Cryptography
- Insecure Authorization
- Client Code Quality
- Code Tampering
- Reverse Engineering
- Extraneous Functionality
“Mobile security testing is the discipline where you think like an attacker, not a tester. The question isn’t whether your app follows the happy path — it’s whether it survives someone actively trying to break it.” — Yuri Kan, Senior QA Lead
iOS Security Testing
Insecure Data Storage (iOS)
// INSECURE: UserDefaults (plaintext storage)
UserDefaults.standard.set("sensitive_token", forKey: "auth_token")
// SECURE: Keychain
import Security
func (as discussed in [OWASP ZAP Automation: Security Scanning in CI/CD](/blog/owasp-zap-automation)) saveToKeychain(key: String, value: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: value.data(using: .utf8)!,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
SecItemAdd(query as CFDictionary, nil)
}
func getFromKeychain(key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecReturnData as String: true
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess, let data = result as? Data {
return String(data: data, encoding: .utf8)
}
return nil
}
Certificate Pinning (iOS)
// Implement certificate pinning
class SecurityManager: NSObject, URLSessionDelegate {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
// Pinned certificate (SHA-256 hash)
let pinnedCertificateHash = "sha256_hash_of_certificate"
let serverCertificateData = SecCertificateCopyData(certificate) as Data
let serverCertificateHash = serverCertificateData.sha256()
if serverCertificateHash == pinnedCertificateHash {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
Testing iOS Apps with Objection
# Install Objection
pip3 install objection
# Connect to iOS device
iproxy 2222 22
# Attach to app
objection --gadget "com.example.app" explore
# Enumerate data storage
ios keychain dump
ios nsuserdefaults get
ios plist cat /path/to/plist
# Bypass jailbreak detection
ios jailbreak disable
# Bypass SSL pinning
ios sslpinning disable
# Monitor crypto operations
ios monitor crypto
Android Security Testing
Insecure Data Storage (Android)
// INSECURE: SharedPreferences (plaintext)
val prefs = getSharedPreferences("app_prefs", MODE_PRIVATE)
prefs.edit().putString("auth_token", "sensitive_token").apply()
// SECURE: EncryptedSharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
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
)
encryptedPrefs.edit().putString("auth_token", "sensitive_token").apply()
Certificate Pinning (Android)
// Network security config (res/xml/network_security_config.xml)
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
(as discussed in [Penetration Testing Basics for QA Testers](/blog/penetration-testing-basics)) <domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set>
<pin digest="SHA-256">base64_encoded_public_key_hash=</pin>
<!-- Backup pin -->
<pin digest="SHA-256">backup_key_hash=</pin>
</pin-set>
</domain-config>
</network-security-config>
// Programmatic certificate pinning with OkHttp
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/primary_key_hash")
.add("api.example.com", "sha256/backup_key_hash")
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
Testing Android Apps with Frida
# Install Frida
pip install frida-tools
# Start Frida server on device
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"
# List running apps
frida-ps -U
# Attach to app
frida -U -f com.example.app
# Bypass root detection
frida -U -l bypass-root.js -f com.example.app
// bypass-root.js - Frida script
Java.perform(function() {
// Hook root detection
var RootDetection = Java.use('com.example.RootDetection');
RootDetection.isRooted.implementation = function() {
console.log('[+] Root detection bypassed');
return false;
};
// Bypass SSL pinning
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {
console.log('[+] SSL pinning bypassed');
return;
};
});
Static Analysis
iOS - MobSF
# Run Mobile Security Framework
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf
# Upload IPA file at http://localhost:8000
# MobSF performs:
# - Binary analysis
# - Plist analysis
# - Hardcoded secrets detection
# - Insecure API usage
# - Code quality issues
Android - APKTool
# Decompile APK
apktool d app.apk -o decompiled
# Analyze manifest
cat decompiled/AndroidManifest.xml
# Search for hardcoded secrets
grep -r "api_key" decompiled/
grep -r "password" decompiled/
# Check for debuggable flag
grep -i "debuggable" decompiled/AndroidManifest.xml
# Rebuild APK (for testing)
apktool b decompiled -o modified.apk
# Sign APK
jarsigner -keystore test.keystore modified.apk alias_name
Dynamic Analysis
Network Traffic Analysis
# Setup proxy (Charles/Burp Suite)
# iOS: Install proxy certificate
Settings → General → Profile → Install Charles Certificate
# Android: Install certificate as user/system CA
adb push charles-ssl-proxying-certificate.pem /sdcard/
Settings → Security → Install from SD card
# Intercept traffic
# 1. Configure device proxy to computer IP:8888
# 2. Bypass certificate pinning (Objection/Frida)
# 3. Monitor API requests in proxy
Runtime Manipulation
# Frida script - Modify method return values
import frida
import sys
device = frida.get_usb_device()
pid = device.spawn(["com.example.app"])
session = device.attach(pid)
script = session.create_script("""
Java.perform(function() {
var MainActivity = Java.use('com.example.MainActivity');
MainActivity.isPremiumUser.implementation = function() {
console.log('[+] isPremiumUser() called');
return true; // Always return premium
};
});
""")
script.load()
device.resume(pid)
sys.stdin.read()
Automated Security Testing
Continuous Security Testing
# .github/workflows/mobile-security.yml
name: Mobile Security Scan
on: [push]
jobs:
android-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build APK
run: ./gradlew assembleRelease
- name: MobSF Scan
run: |
docker run --rm -v $(pwd):/app opensecurity/mobile-security-framework-mobsf \
mobsf --apk /app/app-release.apk --output /app/mobsf-report.json
- name: Check vulnerabilities
run: |
HIGH_VULNS=$(jq '.high_severity_count' mobsf-report.json)
if [ "$HIGH_VULNS" -gt 0 ]; then
echo "Found $HIGH_VULNS high severity issues"
exit 1
fi
Best Practices
Secure Coding Checklist
## Data Storage
- [ ] No sensitive data in UserDefaults/SharedPreferences
- [ ] Use Keychain (iOS) / EncryptedSharedPreferences (Android)
- [ ] Enable file encryption
- [ ] Clear sensitive data from memory
## Network Security
- [ ] Certificate pinning implemented
- [ ] Use TLS 1.2+
- [ ] Validate SSL certificates
- [ ] No cleartext traffic
## Authentication
- [ ] Biometric authentication for sensitive actions
- [ ] Secure token storage
- [ ] Proper session management
- [ ] Auto-logout on inactivity
## Code Protection
- [ ] Obfuscation enabled (ProGuard/R8)
- [ ] Root/Jailbreak detection
- [ ] Tamper detection
- [ ] No hardcoded secrets
Conclusion
Mobile app security testing requires platform-specific knowledge and specialized tools. By combining static analysis, dynamic testing, and runtime manipulation, QA engineers can identify vulnerabilities before attackers exploit them.
Key Takeaways:
- Follow OWASP MASVS guidelines
- Secure data storage with platform encryption APIs
- Implement certificate pinning
- Use MobSF for automated static analysis
- Test with Frida/Objection for runtime analysis
- Integrate security testing in CI/CD
- Never store secrets in code or shared preferences
Official Resources
FAQ
What is the OWASP Mobile Top 10 for mobile security testing?
The OWASP Mobile Top 10 includes: M1 Improper Credential Usage, M2 Inadequate Supply Chain Security, M3 Insecure Authentication/Authorization, M4 Insufficient Input/Output Validation, M5 Insecure Communication, M6 Inadequate Privacy Controls, M7 Insufficient Binary Protections, M8 Security Misconfiguration, M9 Insecure Data Storage, M10 Insufficient Cryptography.
How do I test for insecure data storage in mobile apps?
Check SQLite databases (use DB Browser for SQLite), app logs (adb logcat on Android), SharedPreferences files, and local storage in WebView apps. Use MobSF for automated static analysis. On iOS, check the iOS filesystem using a jailbroken device or Xcode File Browser for sensitive data exposure.
How do I test SSL certificate pinning?
Attempt MITM attacks using Burp Suite with a proxy certificate installed on the device. If SSL pinning is working, requests will fail with certificate validation errors. Use Frida to bypass pinning dynamically for testing: frida-scripts like ‘ssl-kill-switch2’ can disable pinning on jailbroken/rooted devices.
What tools are essential for mobile security testing?
Essential toolkit: MobSF (static and dynamic analysis), Frida (dynamic instrumentation and API hooking), Burp Suite (traffic interception), adb with multiple commands (Android Debug Bridge), Drozer (Android inter-app security), and Objection (runtime mobile exploration without jailbreak).
See Also
- API Security Testing: Complete Guide to OAuth, JWT, and API Keys
- Security Headers Testing: Web Application Protection - Web security headers validation: CSP, HSTS, X-Frame-Options, testing tools,…
- Secure APIs testing: OAuth flows, JWT validation, API key…
- Burp Suite for QA Engineers: Complete Security Testing Guide - Security testing with Burp: proxy setup, scanner, intruder,…
- Load Testing with JMeter: Complete Guide - Apache JMeter mastery: thread groups, samplers, listeners,…
- Grafana & Prometheus: Complete Performance Monitoring Stack - Performance monitoring stack: metrics collection with Prometheus,…
