Mobile Security Testing
Mobile security testing checks that an app protects its users' data on the device, on the network, and where it meets the operating system and other apps. The OWASP Mobile Application Security Verification Standard (MASVS) states what a secure app must do; the OWASP Mobile Application Security Testing Guide (MASTG) explains how to verify it.
Example: a shopping app passes every functional test, yet its refresh token sits in plain text in a preferences file, and a link from any web page opens another customer's order. Only looking at storage and links reveals it.
Why it matters
The company does not control the phone. It can be lost, rooted (unlocked for full system access) or restored from a backup, it joins public Wi-Fi, and anyone can take the app package apart. Whatever the app stores, sends or accepts from other apps is exposed, and one leaked token can mean a taken-over account.
Most first-line checks need no hacking skills, and knowing the platform rules prevents false reports: an app that refuses to talk through your proxy is often doing exactly what it should.
MASVS control groups
MASVS sorts its controls into eight control groups, each named with a MASVS- prefix; MASTG describes how to test them on Android and iOS.
This lesson tests the storage, network and platform groups, where a tester's ordinary tools find real defects. All eight:
MASVS-STORAGE: sensitive data on the device is protected and does not leak.MASVS-CRYPTO: strong algorithms, and no keys hard-coded in the app.MASVS-AUTH: login, sessions and authorisation enforced by the back end; local checks such as biometrics done securely.MASVS-NETWORK: TLS with proper certificate validation, and pinning where the risk calls for it.MASVS-PLATFORM: safe use of links, components, WebViews and the screen.MASVS-CODE: untrusted input is validated; no components with known vulnerabilities.MASVS-RESILIENCE: resistance to reverse engineering and tampering where the risk requires it.MASVS-PRIVACY: only the data the app needs, visible to and controlled by the user.
Local storage: what stays on the device
An app keeps data in its sandbox: preference files in shared_prefs, SQLite databases and files on Android; UserDefaults, files and databases on iOS. The sandbox keeps other apps out, but not someone with a rooted phone, a backup or a debuggable build (a debug build, which lets tools such as run-as reach the app's files; release builds are not debuggable). Data also leaks to logs, the clipboard and the app-switcher screenshot.
MASVS-STORAGE is simple to test: sensitive data is stored only when needed, secrets are kept in the iOS Keychain or encrypted with a key held in the Android Keystore (the platforms' protected stores), logs hold nothing sensitive, and logout removes the session's data. Encoding is not encryption: a Base64 string or a signed JWT in a file is readable by anyone who gets the file. On an Android QA build, look yourself:
# Debuggable QA build, on a test phone or emulator you may use
adb shell run-as com.example.shop ls shared_prefs databases files
adb shell run-as com.example.shop cat shared_prefs/session.xml
# <string name="refresh_token">eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOi...</string>
# ^ a finding: a token in plain text
adb logcat -d | grep -i -E "password|token|bearer|otp"
# Release build: run-as must fail, because the build is not debuggable
adb shell run-as com.example.shop lsTry it: Base64 Decoder Try it: JWT Decoder Try it: JWT Expiration Checker
Worked example: reading an Android app's HTTPS traffic
You have written permission to test your team's shopping app against api.staging.shop.example. An intercepting proxy is a tool that sits between the app and the server and shows every request and response; the lesson on intercepting proxies shows the set-up in depth. Its scope is the set of hosts you are authorised to test, so you put only that host in scope. Its history lists every request it captured, so you can find one later, attach it as evidence or replay it (send it to the server again). The proxy runs on your laptop, listening on the laptop's Wi-Fi address, for example 192.168.1.20:8080. To read HTTPS, the proxy ends the app's TLS connection itself and presents a certificate for the host signed by its own certificate authority (CA). So on a dedicated test phone you set that address as the Wi-Fi proxy and install the proxy's CA certificate as a user certificate.
The app now fails to connect and the proxy logs TLS handshake errors. That is Android working as designed: since Android 7.0 (API level 24), apps that target that level or higher trust only system CAs by default, not CAs a user added. The safe opt-in, shown below, is a debug-overrides block in the network security configuration: it applies only to a debuggable build, so the QA build trusts your proxy CA and the release build keeps the default. With the traffic readable, check:
- Every request uses HTTPS, images and SDKs included. Apps that target Android 9 (API level 28) or higher block plain HTTP by default, so a working
http://call usually means someone allowed cleartext for that host: report it. - Tokens travel in the
Authorizationheader, never in a URL, where servers log them. - After logout, replaying a request with the old token returns
401 Unauthorized.
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<debug-overrides> <!-- applies only when the app is debuggable -->
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
<!-- AndroidManifest.xml -->
<application android:networkSecurityConfig="@xml/network_security_config" ...>Try it: HTTP Headers Parser Try it: API Authentication Tester
Certificate pinning is expected behaviour
Certificate pinning, which MASVS calls identity pinning, means the app accepts only a known certificate, public key or CA for its own API hosts. It protects users from a malicious or unwanted CA on the phone, and it also blocks your proxy: the handshake fails. On a release build that is a pass, so "the app does not work through my proxy" is a false report.
Whether to pin is a risk decision the team records. When the app pins, test the behaviour, not a bypass:
- Through the proxy, the pinned build refuses the connection with a clear error, no crash and no retry over plain HTTP; without the proxy it works.
- An Android release build rejects a user CA anyway, so that failure alone does not prove pinning; ask for a test build that trusts user CAs but keeps the pins.
- Traffic for functional checks comes from the QA build: by default, Android skips the pins in the network security configuration for CAs in
debug-overrides. Pinning written in the app's code needs its own debug switch; agree on it with the developers. Bypassing pinning in a release build is specialist work. - Ask how pins are rotated: Android's documentation recommends always including a backup key, or a server key change cuts off every installed copy until an app update ships.
Platform interaction: links, components, WebViews and the screen
MASVS-PLATFORM covers what the app exchanges with the operating system and other apps. Treat every entry point another app or web page can reach as untrusted input; the commands below open a deep link the way another app would.
- Deep links: any app can open
shop://orders/1043with any value. A protected screen asks for login first, and the server still refuses another user's order. Android App Links and iOS Universal Links are verified against the app's domain; a custom scheme such asshop://is not, and another app can register the same scheme and receive the link, so it must never carry a secret such as a token. - Exported components: Android activities, services, receivers and content providers are exported only when other apps need them, and then check what they receive.
- WebViews load only the app's own or allow-listed pages, never a URL taken from a link parameter.
- The screen: sensitive screens are hidden in the app-switcher preview, and password fields are masked.
# Android test device: open a deep link the way another app would
adb shell am start -W -a android.intent.action.VIEW \
-d "shop://orders/1043" com.example.shop
# iOS simulator
xcrun simctl openurl booted "shop://orders/1043"| Check · MASVS group | Build and method | Expected result |
|---|---|---|
Files the app writesMASVS-STORAGE | QA build: run-as, before and after logout | No password or token in plain text; session data gone after logout |
LogsMASVS-STORAGE | Release build: adb logcat | No tokens, passwords or personal data |
Traffic contentMASVS-NETWORK | QA build through the proxy; debug-overrides trusts the user CA | HTTPS only; tokens in headers, never in URLs |
User CA on the release buildMASVS-NETWORK | Proxy CA installed as a user CA, Android 7+ | Connection refused with a clear error, no crash |
Pinning, if the team chose itMASVS-NETWORK | Test build that trusts user CAs but keeps the pins | Pinned hosts refused through the proxy; the app works without it |
Deep link with another user's idMASVS-PLATFORM | adb shell am start or xcrun simctl openurl | Login first; the server refuses the other user's order |
App-switcher previewMASVS-PLATFORM | Open a sensitive screen, then the app switcher | Content hidden or blurred |
- A refused connection is the pass result
Every check: a dedicated test device, builds from your team and written permission for the back end.
Common mistakes
- Reporting "the app does not work through my proxy" when the Android 7+ trust rule or pinning is doing its job.
- Asking for user-CA trust or disabled pinning in the release build instead of a debug-only configuration.
- Treating the sandbox as safe storage, or a Base64 or JWT value as protected.
- Checking logs only on the QA build, never on the release build, or checking stored data before logout but never after it.
- Opening deep links only with values the app generates, never with another user's id.
- Installing a proxy CA on a personal phone, or testing without written permission.
The QA perspective
Mobile security checks belong in every test cycle, not only in a yearly penetration test. Agree early on two builds: a debuggable QA build that trusts user CAs, and the release candidate exactly as it will ship. Call in a specialist for cryptography, resilience and anything that needs a rooted device or a pinning bypass.
In each report, record the build, device and OS version, the MASVS group, and the file path or request with tokens masked. In refinement, ask what the feature stores, whether the app pins and what logout clears. Test only what you own or have written permission to test.
Practice exercise
Triage the findings of a mobile security session
You have written permission to test your team's shopping app against the staging API with two test accounts. The material lists six observations from the debuggable QA build and the release candidate.
For each one, name the MASVS group, decide whether it is expected, a defect or needs one more check, and say what you do next.
App: shopping app 4.2 for Android, staging API api.staging.shop.example
Test accounts: qa.buyer1, qa.buyer2. Written permission covers both builds.
1. QA build: shared_prefs/session.xml holds refresh_token in plain text.
2. QA build: logcat prints "Authorization: Bearer eyJhbGciOi..." on every call.
3. Release build, proxy CA installed as a user CA: the app shows "Can't connect
securely", the proxy shows TLS handshake errors. Proxy off: the app works.
4. Product images load from http://img.shop.example/p/5531.jpg
5. Logged in as qa.buyer1, adb opens shop://orders/88231 (an order of qa.buyer2):
the order details are displayed.
6. The app-switcher preview of the card payment screen shows the full card number.- Assign each observation to a MASVS group.
- Separate expected platform behaviour from defects.
- For each defect, state the expected secure behaviour and the evidence.
- List your questions for the developers.
Show the model answer
1 (STORAGE): defect, a long-lived token in plain text. Expected: encrypted with an Android Keystore key, or not stored. Attach the path with the value masked, and check whether it survives logout.
2 (STORAGE, logs): one more check. Verbose logging may be intended in a QA build; repeat on the release build, where a token is a defect.
3 (NETWORK): expected. The release build rejects a user CA, as Android 7+ apps do by default, and fails safely. It does not prove pinning; if the app should pin, ask how that was verified.
4 (NETWORK): defect, plain HTTP allowed for that host, so images can be read or altered in transit. Expected: HTTPS only, with no cleartext exception. Evidence: the request in the proxy history.
5 (AUTH, reached through a PLATFORM entry point): defect, probably on the server. Repeat with the QA build and find the order request in the proxy: a 200 OK with qa.buyer2's order is broken object level authorization (BOLA), the server returning a record without checking that it belongs to the caller, so every client of the API is affected. Expected: 403 or 404, and the app shows an error.
6 (PLATFORM, the screen): defect on a payment screen. Expected: the preview is hidden or blurred. Evidence: a screenshot of the app switcher.
Questions for the developers: which data must be stored at all, whether HTTP logging is off in release builds, whether the team decided to pin, and why the image host has a cleartext exception.
Frequently asked questions
Do I need a rooted phone for mobile security testing?
Not for the checks in this lesson. On a debuggable QA build, run-as reads the app's own files on an ordinary test phone or emulator, and the debug network configuration lets your proxy read the traffic. Rooted or jailbroken devices are used in specialist assessments, for example of resilience controls, and only as dedicated test devices.
Why does my proxy show the phone's browser traffic but not the app's?
Since Android 7.0, apps that target API level 24 or higher trust only system CAs unless their network security configuration opts in to user CAs. Ask for a QA build with a debug-overrides block. If the app pins its certificate, any build that keeps the pins shows the same symptom on either platform.
How do I read an iOS app's traffic through the proxy?
Install the proxy CA on a dedicated test iPhone as a profile, then switch on full trust for it in Settings under "General > About > Certificate Trust Settings". Apps accept a fully trusted CA unless they pin. If a pinned build refuses the connection, that is the expected result; ask the developers for a test build whose traffic you may inspect.
Does MASVS require certificate pinning?
MASVS covers identity pinning in its network group, but whether a particular app must pin is a risk decision; many apps rely on correct TLS certificate validation alone. Find out what the team decided and test that the app behaves accordingly, including a safe failure and a plan for rotating keys.
References
- OWASP Mobile Application Security Verification Standard (MASVS)OWASP's security requirements for mobile apps, grouped into control areas such as storage, cryptography, authentication, network and platform interaction.
- OWASP Mobile Application Security Testing Guide (MASTG)The testing manual that goes with MASVS: techniques and test cases for verifying mobile app security.
- About App LinksAndroid App Links: deep links to your own website that the system verifies through a Digital Asset Links file (assetlinks.json), so they open the app directly.
- Certified Tester Mobile Application Testing (CT-MAT)ISTQB's specialist syllabus on mobile testing: app types, device and platform challenges, test types and tools.
Practise with Mutqan tools
Decode Base64 or Base64url strings to text, with automatic padding repair.
Decode JWT header and payload, inspect claims and check expiration — entirely in your browser.
Check whether a JWT is expired, not yet valid, or how long remains until expiry.
Parse raw HTTP request/response headers into a structured table with explanations.
Test Bearer, Basic, API-key and custom-header authentication against an endpoint and compare authorised vs unauthorised responses.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.