ADR Reference: ADR-SEC-011
Mobile application security differs from backend security because the attack surface is distributed across millions of untrusted devices outside the engineering team's control. Every mobile binary is a potential attack artefact — decompilable, modifiable, repackageable. Every on-device data store is a breach target if the device is compromised. Security architecture for mobile must treat device compromise as a design input, not an edge case.
| Field |
Value |
| ADR Reference |
ADR-SEC-011 |
| Version |
1.0 |
| Date Raised |
May 2025 |
| Review Date |
November 2025 |
| Author |
Solutions Architecture Practice & Security Architecture — Ascendion |
| Status |
ACCEPTED |
| Domain |
Mobile Security Architecture |
| ARB Approval |
Required — Security Architect Sign-off Mandatory |
| Stakeholders |
Security Architects · Mobile Engineering Leads · Compliance Officers · Penetration Testers · Client CISO |
Executive Summary
Decision: All Ascendion-delivered mobile applications must implement controls addressing the OWASP Mobile Top 10 (2024 edition) as a mandatory baseline. OWASP MASVS Level 1 is the minimum compliance target for all applications. Level 2 is mandatory for financial services, healthcare, and government applications. Controls are enforced through code review gates, CI/CD security scanning, and Security Architecture Review Board sign-off before first production release. OAuth 2.0 + PKCE is the mandatory authentication flow. Hardware-backed credential storage is mandatory for all sensitive data. Certificate pinning using SPKI hashes is mandatory for financial and healthcare applications.
Decision Drivers
| Priority |
Quality Attribute |
Weight |
Rationale |
| 1 |
Credential Protection |
24% |
Tokens and passwords must never be recoverable from a compromised device. |
| 2 |
Authentication Integrity |
22% |
No client_secret in binary. PKCE eliminates static secrets. Biometric MFA for high-risk operations. |
| 3 |
Transport Security |
18% |
TLS 1.3 + SPKI pinning prevents interception and MITM on hostile networks. |
| 4 |
Binary & Runtime Protection |
16% |
The binary is distributed to untrusted devices. R8 hardening + attestation detect tampering. |
| 5 |
Regulatory Compliance |
12% |
MASVS, BSP Circular 982, PCI-DSS v4.0, HIPAA are contractual obligations for regulated verticals. |
| 6 |
Data-at-Rest Encryption |
8% |
On-device stores must be inaccessible at rest until the device is unlocked. |
Considered Options
Option A — OWASP MASVS 2.0 + Mobile Top 10 Layered Controls (ADOPTED)
Full defense-in-depth across transport, authentication, storage, and binary layers, enforced by code-review gates, CI scanning, and SARB sign-off. MASVS Level 1 baseline for all apps, Level 2 for regulated verticals. Weighted score: 4.87 / 5.0
Option B — Transport + Authentication Only
TLS and OAuth implemented, but no hardware-backed storage hardening, binary protection, or attestation. Fails MASVS Level 2 and the OWASP Mobile Top 10 (M7, M9). Weighted score: 2.81 / 5.0 — DISMISSED
Security implemented per-developer without a governing standard or review gate. Unauditable, non-compliant, and unacceptable for any production application. Weighted score: 1.40 / 5.0
Decision
OWASP MASVS-aligned layered controls are mandatory. MASVS Level 1 is the minimum for all applications; Level 2 is mandatory for financial services, healthcare, and government. OAuth 2.0 + PKCE is the only permitted authentication flow. Hardware-backed credential storage and SPKI certificate pinning are mandatory for regulated applications. The OWASP Mobile Top 10 control matrix below is the binding control set.
OWASP Mobile Top 10 — Control Matrix
| OWASP |
Risk |
Required Control |
Implementation |
| M1 |
Improper Credential Usage |
No hardcoded credentials. CI secret scanning. |
GitLeaks pre-commit + CI. Android Keystore / iOS Keychain. |
| M2 |
Inadequate Supply Chain |
SDK risk assessment. Dependency checksums. |
Gradle verification. SPM checksum. OWASP Dependency Check CI. |
| M3 |
Insecure Authentication |
OAuth 2.0 + PKCE mandatory. No Implicit Flow. |
AppAuth-Android and AppAuth-iOS libraries. |
| M4 |
Insufficient Input Validation |
Parameterised queries. WebView JS disabled. |
Room parameterised queries. allowList for WebView URL schemes. |
| M5 |
Insecure Communication |
SPKI hash certificate pinning. TLS 1.2 minimum. |
OkHttp CertificatePinner. iOS URLSession challenge handler. |
| M6 |
Inadequate Privacy Controls |
PII never in logs. Minimum permissions. |
@Pii annotation + ProGuard rule. @Masked property wrapper. |
| M7 |
Insufficient Binary Protections |
debuggable: false. R8 full mode. |
CI build verification. R8 configuration review. |
| M8 |
Security Misconfiguration |
cleartext blocked. No debug endpoints in release. |
Network Security Config. ATS enforced. |
| M9 |
Insecure Data Storage |
No sensitive data in SharedPreferences / UserDefaults. |
EncryptedSharedPreferences. Keychain kSecAttrAccessibleWhenUnlockedThisDeviceOnly. |
| M10 |
Insufficient Cryptography |
AES-256-GCM. No MD5/SHA1/ECB. |
Detekt CryptoRule. SwiftLint cryptography rules. |
Authentication Architecture: OAuth 2.0 Authorization Code + PKCE is the mandatory authentication flow. No client_secret in mobile binary — extractable from APK with jadx in under 60 seconds. PKCE eliminates the static secret requirement. Client generates code_verifier (128 random characters), hashes with SHA-256 to code_challenge, server verifies the hash on token exchange. Biometric MFA mandatory for financial transactions and PHI access: BiometricPrompt API on Android with BIOMETRIC_STRONG requirement; LocalAuthentication on iOS with deviceOwnerAuthenticationWithBiometrics. Biometric-bound keys: KeyStore setUserAuthenticationRequired(true) on Android, kSecAccessControlBiometryCurrentSet on iOS.
Runtime Attestation: Google Play Integrity API: MEETS_STRONG_INTEGRITY verdict verifies genuine Play-distributed app on genuine unmodified Android. Call on every sensitive transaction. Apple AppAttest: generates hardware-bound attestation key in SecureEnclave, server verifies assertion signature. Both mandatory for financial services and healthcare applications.
Trade-off Analysis
| Trade-off Accepted |
Consequence |
Mitigation |
| Hardware-backed storage adds implementation complexity |
Keystore/Keychain APIs are verbose and device-variable |
core-security module abstracts storage; StrongBox/SecureEnclave used where available with documented fallback |
| SPKI certificate pinning can break on certificate renewal |
A mis-managed rotation can lock users out |
Pin the SPKI hash (survives cert renewal); maintain backup pins; rotation runbook reviewed by Security Architect |
| Per-transaction attestation adds latency |
Play Integrity / AppAttest calls cost round-trip time |
Attestation called only on sensitive transactions, not every request; results cached within session window |
Implementation Guidance
Secure storage is selected by data classification. The PROHIBITED row is a blocking code-review finding.
| Data Classification |
Android Mechanism |
iOS Mechanism |
Accessibility |
| Credentials (tokens, passwords) |
Android Keystore — hardware-backed AES-256 |
Keychain — kSecAttrAccessibleWhenUnlockedThisDeviceOnly |
When device unlocked, not backed up, device-bound |
| Sensitive user prefs |
EncryptedSharedPreferences |
Keychain — kSecAttrAccessibleWhenUnlocked |
When device unlocked |
| Sensitive structured data |
Room + SQLCipher or EncryptedFile |
SwiftData with FileProtection .complete |
Inaccessible at rest until device unlocked |
| Non-sensitive prefs |
SharedPreferences — plaintext acceptable |
UserDefaults — plaintext acceptable |
No encryption required |
| PROHIBITED |
SharedPreferences for credentials |
UserDefaults for credentials |
P1 finding — blocking code review gate |
- Implement OAuth 2.0 + PKCE via AppAuth-Android / AppAuth-iOS — no client_secret in the binary
- Store all credentials in Android Keystore / iOS Keychain per the classification table above
- Encrypt structured data at rest with SQLCipher (Android) or FileProtection .complete (iOS)
- Configure SPKI hash certificate pinning with backup pins; enforce TLS 1.3
- Enable R8 full mode, debuggable:false, and @Pii / @Masked log stripping
- Integrate Play Integrity API and Apple AppAttest, called on sensitive transactions
- Add GitLeaks and OWASP Dependency Check to the CI pipeline (ADR-MOB-003)
Compliance Checkpoints
MASVS level is determined by application vertical. Level 2 verticals carry additional mandatory requirements.
| Application Category |
MASVS Level |
Additional Requirements |
| Financial Services (banking, payments) |
Level 2 |
BSP Circular 982 · PCI-DSS · Root detection · Cert pinning · Biometric MFA |
| Healthcare (PHI handling) |
Level 2 |
HIPAA · Automatic logoff 5 min · Audit logging · Jailbreak detection |
| Government / Public Sector |
Level 2 |
PDPA Philippines · Data residency · Government PKI |
| Enterprise Internal Apps |
Level 1 |
MDM/MAM enrollment · AppConfig · Corporate VPN |
| Consumer / Commercial Apps |
Level 1 |
Standard OWASP controls baseline |
| Gate |
Trigger |
Owner |
SLA |
| Security Architecture Review |
Before first external API integration |
Security Architect |
Blocking |
| Credential Storage Code Review |
Every PR touching auth or storage |
Security-trained Tech Lead |
Blocking |
| OWASP Dependency Check |
Every CI build — automated |
CI Pipeline |
Blocking on P1/P2 CVE |
| Penetration Test |
Before first production release |
External Security Firm |
Blocking for L2 apps |
| BSP Circular 982 Compliance Review |
Philippines banking apps |
Compliance Officer + Security Architect |
Blocking |
| Reference |
Title |
Relationship |
| ADR-MOB-001 |
Mobile Architecture Pattern |
Security controls designed around Clean Architecture layer boundaries |
| ADR-MOB-002 |
Mobile Platform Selection |
Platform-specific attestation (Play Integrity vs AppAttest) |
| ADR-MOB-003 |
Mobile CI/CD Pipeline |
Dependency CVE scanning and security lint gates |
| ADR-INT-005 |
BFF API Design |
OAuth 2.0 PKCE token flows require BFF endpoint alignment |
Flowchart
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%%
flowchart TD
subgraph L1["🌐 Layer 1 — Transport Security"]
direction LR
TLS_S["<b>TLS 1.3 Mandatory</b><br/>Cleartext HTTP blocked<br/>Network Security Config (Android)<br/>App Transport Security (iOS)"]
SPKI["<b>SPKI Hash Certificate Pinning</b><br/>OkHttp CertificatePinner<br/>iOS URLSession challenge handler<br/>Survives certificate renewal"]
end
subgraph L2["🔑 Layer 2 — Authentication — OAuth 2.0 + PKCE"]
direction LR
PKCE_S["<b>OAuth 2.0 + PKCE</b><br/>AppAuth-Android · AppAuth-iOS<br/>code_verifier on-device<br/>NO client_secret in binary"]
BIO_S["<b>Biometric MFA</b><br/>BiometricPrompt BIOMETRIC_STRONG<br/>LocalAuthentication iOS<br/>Step-up for high-risk ops"]
TOKEN_S["<b>Token Architecture</b><br/>Access: memory only · 15 min<br/>Refresh: Keystore/Keychain<br/>Session: 5 min inactivity"]
end
subgraph L3["🗄 Layer 3 — Secure Storage"]
direction LR
AKS_S["<b>Android Keystore</b><br/>Hardware-backed AES-256<br/>setUserAuthenticationRequired<br/>StrongBox where available"]
IOK_S["<b>iOS Keychain</b><br/>kSecAttrAccessibleWhenUnlockedThisDeviceOnly<br/>Not backed up · Device-bound<br/>SecureEnclave for biometric keys"]
EDB["<b>Encrypted Database</b><br/>SQLCipher + Room (Android)<br/>SwiftData FileProtection .complete<br/>Full purge on secure logout"]
end
subgraph L4["🛡 Layer 4 — Binary Protection"]
direction LR
R8_S["<b>Binary Hardening</b><br/>R8 Full Mode · debuggable:false<br/>@Pii annotation strips logs<br/>Exported Activities restricted"]
PI_S["<b>Play Integrity API</b><br/>MEETS_STRONG_INTEGRITY<br/>Called on every transaction<br/>Genuine device · Genuine app"]
AA_S["<b>Apple AppAttest</b><br/>SecureEnclave attestation key<br/>Server verifies assertion<br/>DeviceCheck fraud signal"]
end
subgraph MASVS_S["📋 MASVS Compliance"]
direction LR
L1_APP["<b>MASVS Level 1</b><br/>All applications<br/>Baseline OWASP controls"]
L2_APP["<b>MASVS Level 2</b><br/>Financial · Healthcare · Gov<br/>BSP Circular 982 · PCI-DSS<br/>External pentest required"]
end
L1 --> L2 --> L3 --> L4 --> MASVS_S
style L1 fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
style L2 fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style L3 fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style L4 fill:#FEE2E2,stroke:#DC2626,stroke-width:2px
style MASVS_S fill:#F3E8FF,stroke:#7C3AED,stroke-width:2px
style TLS_S fill:#BBF7D0,stroke:#16A34A,color:#14532D
style SPKI fill:#BBF7D0,stroke:#16A34A,color:#14532D
style PKCE_S fill:#BFDBFE,stroke:#2563EB,color:#1e3a5f
style BIO_S fill:#BFDBFE,stroke:#2563EB,color:#1e3a5f
style TOKEN_S fill:#93C5FD,stroke:#1D4ED8,color:#1e3a5f
style AKS_S fill:#FDE68A,stroke:#D97706,color:#78350F
style IOK_S fill:#FDE68A,stroke:#D97706,color:#78350F
style EDB fill:#FCD34D,stroke:#B45309,color:#78350F
style R8_S fill:#FCA5A5,stroke:#DC2626,color:#7f1d1d
style PI_S fill:#FCA5A5,stroke:#DC2626,color:#7f1d1d
style AA_S fill:#FCA5A5,stroke:#DC2626,color:#7f1d1d
style L1_APP fill:#E9D5FF,stroke:#7C3AED,color:#4C1D95
style L2_APP fill:#C4B5FD,stroke:#6D28D9,color:#4C1D95
References
- OWASP — MASVS 2.0. owasp.org/www-project-mobile-app-security
- OWASP — Mobile Top 10 (2024). owasp.org/www-project-mobile-top-10
- IETF RFC 7636 — PKCE. tools.ietf.org/html/rfc7636
- BSP Circular 982. bsp.gov.ph
- Google — Play Integrity API. developer.android.com/google/play/integrity
- Apple — AppAttest. developer.apple.com/documentation/devicecheck
⚠ Credentials in SharedPreferences / UserDefaults — Session tokens stored in plaintext storage readable on rooted/jailbroken devices. P1 security finding.
CORRECT: Android Keystore + EncryptedSharedPreferences. iOS Keychain with kSecAttrAccessibleWhenUnlockedThisDeviceOnly.
⚠ Client Secret in Mobile Binary — OAuth client_secret embedded in BuildConfig, plist, or strings.xml. Extractable in under 60 seconds.
CORRECT: OAuth 2.0 + PKCE eliminates the client_secret requirement. If truly required, fetch from backend post-authentication only.
⚠ Sending Raw Biometrics to Cloud — Transmitting facial images or fingerprint data to a cloud API creates a high-value breach target.
CORRECT: On-device biometric verification using Face ID/Touch ID (iOS) or BiometricPrompt (Android). Biometrics never leave the device.
End of Document
Maintained by
Architecture Council
Review Cadence
Annually or upon any
Tier 2 architectural change
Change Requests
Submitted as RFC to the
Architecture Council for review
Version History
Maintained in version
control alongside this document