A mobile app takes data from far more places than a web form, and every one of them (deep links, intents from other apps, WebView messages, files, push payloads and API responses) can carry input the app was never designed to handle. OWASP lists this as M4: Insufficient Input/Output Validation in the Mobile Top 10 2024, covering injection into local databases, script injection through WebViews, path traversal and unchecked data integrity. This guide goes deeper than the overview: it explains what M4 covers and what it does not, the MASVS controls that turn it into testable requirements, the platform APIs that cause and fix most findings on Android and iOS, and how to test, measure and map the work to other frameworks. It is written for mobile developers, application security engineers and testers who own or assess an app. For the full list, see the OWASP Mobile Top 10 Guide.
Key takeaways
- Every entry point is untrusted. MASVS-CODE-4 names the UI, IPC, network and file system as data entry points, and says data from all of them must be verified and sanitised before use.
- Validation is not the injection fix. Parameterised queries, context-aware output encoding and safe parsers stop injection; input validation narrows what reaches them but does not replace them.
- The WebView is where M4 does most damage. A JavaScript bridge exposed to any frame turns a script injection into a call into native code with the app's permissions.
- Other apps are attackers too. Exported activities, content providers, deep links and nested intents let a malicious app on the same device deliver input, which is why MASVS-PLATFORM-1 sits beside CODE-4.
- The backend must validate again. Any check in the app can be removed on a rooted or jailbroken device, so the server treats every request from the app as untrusted input.
What it is and who it applies to
OWASP's M4 page describes the risk as weak validation and sanitisation of external data, such as user input or network data, exposing the app to SQL injection, command injection and cross-site scripting (XSS), with poor output validation able to corrupt data or let attackers change what users see. OWASP rates exploitability as difficult, prevalence as common, detectability as easy and impact as severe. The page splits the weakness into four parts:
- Insufficient input validation: unexpected or malicious input passes into app logic.
- Insufficient output validation: data the app generates or renders carries injected script, for example HTML shown in a WebView.
- Lack of contextual validation: input that is safe in one context reaches a query, file path or format-string function where it is not.
- Failure to validate data integrity: tampered or malformed data is processed as if it were genuine.
M4 applies to every native, hybrid and cross-platform app, but the exposure grows with three features: WebViews that render remote or user-generated content, exported components or deep links that accept parameters, and local databases or files built from external data. M4 does not cover TLS and certificate checks (that is M5, see the OWASP Mobile Top 10 M5 Insecure Communication Guide), exported components left open by configuration (M8), or authorisation decisions (M3). The test for M4 is whether the app handles the content of the data safely once it arrives.
Structure and key requirements
The Top 10 is an awareness list. The requirements live in the OWASP Mobile Application Security Verification Standard (MASVS), and the tests in the Mobile Application Security Testing Guide (MASTG). Four MASVS controls carry most of M4.
| MASVS control | Control statement | What it means for M4 |
|---|---|---|
| MASVS-CODE-4 | The app validates and sanitizes all untrusted inputs. | Covers injection (SQL, XSS, insecure deserialisation) and bypass of security checks from any entry point |
| MASVS-PLATFORM-1 | The app uses IPC mechanisms securely. | Intents, content providers, deep links and URL schemes deliver input from other apps |
| MASVS-PLATFORM-2 | The app uses WebViews securely. | JavaScript bridges, file access and the content a WebView may load |
| MASVS-CODE-3 | The app only uses software components without known vulnerabilities. | Parsers, image and media libraries and WebView engines are where malformed input is most often exploited |
The OWASP M4 prevention list reduces to six requirements: validate input with strict rules and length limits, sanitise and encode output, validate by context, check data integrity, use parameterised queries and prepared statements, and test regularly.
Entry points and the defence each needs
| Entry point | Typical flaw (CWE) | Primary defence |
|---|---|---|
| Local SQLite or content provider query | SQL injection (CWE-89) | ? placeholders with selectionArgs, Room DAO bound parameters, SQLiteQueryBuilder.setStrict(true) |
| WebView rendering remote or user content | XSS (CWE-79) | Context-aware output encoding, HTML sanitisation library, JavaScript off unless needed |
| WebView to native bridge | Exposed native functionality | addWebMessageListener with an origin allowlist, payload validation |
| File names from intents, providers or downloads | Path traversal (CWE-22) | Canonicalise and confirm the target stays in the expected directory |
| Deep links and custom URL schemes | Improper input validation (CWE-20) | Verified App Links or universal links, allowlist of parameter values, state checks |
| Nested intents in extras | Intent redirection | resolveActivity() target check, IntentSanitizer, PendingIntent |
| Serialised objects, JSON, XML from network or storage | Insecure deserialisation (CWE-502) | Typed schemas, restrictive parsers, no native object deserialisation of untrusted data |
| Native code (C, C++) taking strings | Format string (CWE-134), memory corruption | Constant format strings, bounds checks, memory-safe wrappers |
Android specifics
- Local SQL: Android's SQL injection guidance names string concatenation into
rawQuery()as the pattern to avoid. Use?placeholders,SQLiteDatabase.query()with separateselectionandselectionArgs, or Room, which checks queries at compile time. For content providers, configureSQLiteQueryBuilderwithsetStrict(true),setStrictColumns(true)against asetProjectionMap, andsetStrictGrammar(true). Android also warns against several providers with different permission levels sharing one SQLite file, because an injection in one reaches the other tables. - Paths: resolve every file name with
File.getCanonicalPath()and confirm it stays inside the expected directory before reading, writing or changing permissions. Compare against the directory plus a trailing separator, so a sibling such asfiles_evildoes not pass a prefix check. See the Path Traversal Guide. - Deep links: set
android:autoVerify="true"on the intent filter so the system verifies domain ownership and another app cannot hijack the link, then validate every parameter against an allowlist and check authentication state before acting. - Intent redirection: code that calls
getParcelableExtra()and passes the result tostartActivity()lets another app reach private components. Check the target withresolveActivity(), sanitise withIntentSanitizer(including theFLAG_GRANT_*_URI_PERMISSIONflags), or use aPendingIntent. Android 16 adds default protection against intent redirection; opting out withremoveLaunchSecurityProtection()should be rare and reviewed. - WebView bridges: Android's guidance ranks
addWebMessageListener(withallowedOriginRules) abovepostWebMessageandaddJavascriptInterface. The legacy interface has no origin checks and is available to every frame, including iframes, and the guidance says not to rely onWebView.getUrl()to identify the caller.
iOS specifics
- WebViews: use
WKWebView(the olderUIWebViewis deprecated), keep JavaScript off where pages do not need it, and treatWKScriptMessageHandlermessages as untrusted input: check the frame's origin and validate the message body. - Links: prefer universal links, which are tied to a domain through the apple-app-site-association file, over custom URL schemes that any app can claim, and validate every parameter in the handler.
- Parsing: decode into typed models (
Codable) and useNSSecureCodingwhen archiving objects, rather than accepting arbitrary object graphs. - Language-level safety: see the Swift and iOS Secure Coding Guide and, for Android, the Kotlin and Android Secure Coding Guide.
Identity and NHI requirements
M4 matters for identity because the data flowing through unvalidated entry points is often a credential. Deep links carry OAuth authorisation codes and magic-link tokens, WebView bridges are often added to hand a session token from native code to a web page, and local databases hold cached tokens and API responses.
- Never expose tokens through a bridge: a bridge method such as
getToken()reachable from any frame gives an XSS on any loaded page the user's session. Limit bridges to named operations and allowlisted origins. - Bind redirect handling: OAuth redirects should use verified App Links or universal links plus PKCE, so a hijacked custom scheme cannot capture a usable code. See the Token and Session Security Guide.
- Treat API responses as input: data returned by your own or a third-party API can be attacker-controlled (another user's profile text, a compromised partner). This is the client side of the server-side pattern in the OWASP API Top 10 API10 Unsafe Consumption of APIs Guide.
- Keep embedded keys out of reach: injection in a WebView or provider can read anything the app can read, including embedded API keys. The fix for those belongs to M1: see the OWASP Mobile Top 10 M1 Improper Credential Usage Guide.
Implementation steps
- Inventory entry points: list every exported component, intent filter, URL scheme, App Link or universal link domain, WebView and the URLs it loads, bridge method, file import path, push payload handler and API consumed. Generate the Android list from the merged manifest and the iOS list from Info.plist and entitlements, so it stays current.
- Classify sinks: for each entry point, note where the data ends up: SQL, file path, WebView, native code, intent launch, or business logic. The sink decides the defence, not the source.
- Fix the sinks first: replace concatenated SQL with bound parameters, move WebView bridges to
addWebMessageListeneror origin-checked message handlers, canonicalise file paths, and validate nested intents. These changes remove whole classes of bug. - Add validation at the boundary: per entry point, define types, lengths, formats and allowlisted values. The OWASP Input Validation Cheat Sheet asks for full-string matches, bounded length before regular expressions to avoid ReDoS, and one Unicode normalisation policy applied before validation.
- Encode output by context: HTML, JavaScript and URL contexts need different encoding; for user-authored HTML use a maintained sanitisation library rather than regular expressions. See the Input Validation and Output Encoding Guide.
- Check integrity: sign or MAC data the app must trust after it leaves your control (cached configuration, downloaded content packs), and reject anything that fails verification.
- Test in report-only mode first: run static analysis rules for
rawQueryconcatenation,addJavascriptInterface,setJavaScriptEnabled(true)and uncheckedgetParcelableExtrain CI as warnings, triage false positives, then make high-confidence rules blocking. - Roll out safely: release fixes through staged rollout on Google Play and phased release on the App Store, watch crash rates and backend validation errors, and keep the previous build ready to halt. Where a stricter validator breaks legitimate data, loosen the allowlist in a hotfix rather than removing validation.
Choosing the approach
| Situation | Prefer | Trade-off |
|---|---|---|
| WebView shows only your own bundled pages | Load from app assets, JavaScript only where needed, no remote navigation | Content updates need an app release |
| WebView shows remote pages you control | Origin allowlist on bridge and navigation, HTTPS only, CSP on the pages | XSS on your own domain still reaches the bridge, so validate payloads |
| WebView shows third-party or user content | No bridge at all; open in the system browser or a custom tab instead | Less control over the user experience |
| Small team, single app | Room or Core Data, platform link verification, one shared validation module | Less flexibility in queries |
| Large portfolio or shared SDK | Central validation library, lint rules in every pipeline, MASVS-CODE-4 and PLATFORM-1/2 in supplier contracts | Up-front engineering and rule tuning |
Evidence and assessment
MASTG organises tests by MASVS control, so assessors ask for evidence against CODE-4, PLATFORM-1 and PLATFORM-2. Useful evidence is concrete and repeatable:
- Entry point register: the inventory from step 1, with owner, sink and defence for each row.
- Static analysis results: per-release scanner output on the release binary, with suppressions justified.
- Dynamic tests: crafted deep links, intents and provider queries sent from a test app or
adbon a test device, and WebView content tested for script execution and bridge access. The Mobile App Security Testing Guide covers tooling. - Backend logs: server-side validation rejects per endpoint and per app version, which show whether malformed data is arriving.
Detection on the device is limited, so monitor where you can see: backend validation failures, crash clusters on parser code paths, unexpected App Link verification failures in the Play Console, and unusual deep link parameters reaching analytics.
| Metric | What good looks like |
|---|---|
| Entry points with a documented sink and defence | 100% of exported components, links and bridges |
Concatenated SQL or addJavascriptInterface uses in release builds | Zero, or each with an approved exception |
| WebView bridges with an origin allowlist | All bridges |
| Open M4 findings past remediation SLA | None; trend falling per release |
| Backend rejects for malformed app input | Low and stable; spikes investigated |
Mapping to other frameworks
| Framework | Reference | Relevance to M4 |
|---|---|---|
| OWASP MASVS | MASVS-CODE-4, MASVS-PLATFORM-1, MASVS-PLATFORM-2 | Testable requirements behind M4 |
| OWASP ASVS 5.0 (backend and web content) | V1.2.1, V1.2.3, V1.2.4, V1.3.1, V2.2.1, V2.2.2, V5.3.2 | Output encoding, parameterised queries, HTML sanitisation, allowlist validation at a trusted layer, safe file paths. See the OWASP ASVS Guide |
| NIST SP 800-53 Rev. 5 | SI-10, SI-15, SA-11, SC-18 | Input validation, output filtering, developer testing, mobile code |
| NIST CSF 2.0 | PR.PS-06, PR.DS-10 | Secure development practice, data in use |
| ISO/IEC 27001:2022 Annex A | A.8.28 Secure coding | Secure coding principles applied to development |
| CIS Controls v8.1 | Control 16 Application Software Security | Secure development and testing of in-house software |
| MITRE CAPEC | CAPEC-66 SQL Injection, CAPEC-63 Cross-Site Scripting (XSS), CAPEC-126 Path Traversal, CAPEC-153 Input Data Manipulation | Attack patterns M4 defends against |
| MITRE ATT&CK Mobile | T1664 Exploitation for Initial Access, T1658 Exploitation for Client Execution | How adversaries use input-handling flaws on mobile |
Common pitfalls
- Validating only in the UI: form fields are checked but the same value arrives unchecked through a deep link, intent extra or restored state. Validate at the component that uses the data.
- Denylisting characters instead of binding parameters: stripping quotes breaks valid names and still leaves SQL injection. The Input Validation Cheat Sheet is explicit that cleaning input does not provide parameterisation or encoding.
- A token bridge open to every frame:
addJavascriptInterfaceadded to pass a session to a help page, then an advert or third-party iframe loads in the same WebView and can call it. - Prefix-only path checks:
startsWithon a directory without a trailing separator lets a sibling directory through, and checks done before canonicalisation miss../sequences. - Trusting your own API: responses are rendered as HTML because "the server is ours", while the server is storing text another user wrote.
- Relying on client validation for business rules: limits, prices and quantities checked only in the app are edited on a rooted device; the backend must enforce them. The OWASP API Top 10 API6 Unrestricted Access to Sensitive Business Flows Guide covers the server side.
Practitioner checklist
- Maintain a register of every entry point (components, links, schemes, bridges, file imports, push handlers) with its sink and defence.
- Use bound parameters or Room for all local SQL, and strict
SQLiteQueryBuildersettings for content providers. - Replace
addJavascriptInterfacewithaddWebMessageListenerand an HTTPS origin allowlist; never expose tokens or generic file or execute methods. - Canonicalise every file path and confirm it stays in the expected directory.
- Verify App Links (
android:autoVerify) and universal links, and allowlist deep link parameters. - Validate nested intents with
resolveActivity()orIntentSanitizer, or usePendingIntent. - Encode output by context and sanitise user HTML with a maintained library.
- Run M4 lint rules in CI, start as warnings, then block on high-confidence rules.
- Repeat validation on the backend for every value the app sends.
- Test CODE-4 and PLATFORM-1/2 with MASTG methods before major releases.
Standards and references
- OWASP: Mobile Top 10 2024 M4 Insufficient Input/Output Validation
- OWASP: Mobile Application Security Verification Standard
- OWASP: Input Validation Cheat Sheet
- OWASP: Application Security Verification Standard 5.0
- Android Developers: SQL injection
- Android Developers: Native API access through JavaScript bridges
- Android Developers: Intent redirection
- Android Developers: Unsafe use of deep links
Related NHI Mgmt Group resources: OWASP Mobile Top 10 Guide · Mobile App Security Guide · Input Validation and Output Encoding Guide · OWASP Mobile Top 10 M5 Insecure Communication Guide · Cross-Site Scripting (XSS) Prevention Guide