Skip to main content

Mobile Security

Mobile Security

Project: {{PROJECT_NAME}}Drop — Fintech Payment App Version: {{VERSION}}0.1.0 Date: {{DATE}}2026-02-23 Author: {{AUTHOR}}John (AI Director, ALAI) Status: Draft | In Review | Approved Reviewers: {{REVIEWERS}}Alem Bašić (CEO)

Document History

Version Date Author Changes
0.1 {{DATE}}2026-02-23 {{AUTHOR}}John Initial draft — current state analysis + Phase 2 security roadmap

1. Mobile Threat Model

Context: Drop is a PSD2-regulated payment app. It handles real money movements from users' bank accounts. Drop never stores funds, but it stores authentication tokens that could initiate payments if compromised.

logs
Threat Actor Goal Attack Vector Likelihood Impact Mitigation
Malicious app on device Steal authBearer tokenstoken Shared storageAsyncStorage access Medium HighCritical SecurePhase storage2: (Keychain/Keystore)migrate token to expo-secure-store
Network interceptor (MITM) Intercept payment API calls Rogue WiFi, proxy Medium HighCritical CertificateTLS 1.3 enforced; certificate pinning Phase 2
Reverse engineer Extract API keys, business logic APK/IPA decompilation Medium Medium CodeHermes obfuscation,bytecode; no hardcoded secrets
Stolen/lost device AccessInitiate userpayments dataas victim Physical access + stored token High HighCritical BiometricPhase 2: biometric lock, datatoken encryption, remote wipebinding
Rooted/jailbroken device Bypass security controls OS privilege escalation Low High Root/jailbreak detection Phase 2
MaliciousSession insiderhijacking AccessReplay userstolen datatoken SourceNetwork code accesscapture Low HighCritical SecretsToken inexpiry vault,(7 nodays); PIIHTTPS inenforced
Social engineeringSteal BankID credentialsPhishingMediumCriticalUser education; BankID handles its own auth

Assets to protect:protect (priority order):

  1. AuthBearer tokenstoken (accessallows +payment refresh)initiation from user's bank account)
  2. User PII (name, email, paymentphone info)— BankID-verified identity)
  3. APITransaction keyshistory and(financial secretsdata)
  4. CachedExchange sensitiverate dataAPI access
  5. App integrity (prevent tampered builds)

2. Authentication

2.1 Biometric Authentication

Current state (Phase 1): Not implemented. Login is email + password only.

Implementation:Phase 2 implementation: {{expo-local-authentication | react-native-biometrics}}

// Phase 2 — lib/biometrics.ts
import * as LocalAuthentication from 'expo-local-authentication';

export async function authenticateWithBiometrics(): Promise<boolean> {
  const hasHardware = await LocalAuthentication.hasHardwareAsync();
  const isEnrolled = await LocalAuthentication.isEnrolledAsync();

  if (!hasHardware || !isEnrolled) {
    return promptPINFallback();
  }

  const result = await LocalAuthentication.authenticateAsync({
    promptMessage: 'VerifyBekreft youridentiteten identity'din',
    fallbackLabel: 'UseBruk PIN',
    cancelLabel: 'Cancel'Avbryt',
    disableDeviceFallback: false,
  });

  return result.success;
}

UsePlanned biometric use cases for(Phase biometric auth:2):

  • App unlock after backgrounding > {{5 minutes}}
  •  View sensitive data (payment info, full account number)minutes
  • Confirm high-value remittance transactions (amount > {{$100}})5000 NOK)
  • Change security settings
  •  View full transaction history

Fallback: 4-digit PIN code(same as web registration flow){{expo-secure-store}}hash stored hashin expo-secure-store (bcrypt, not reversible)


2.2 Session Management

Property ValueCurrent (Phase 1)Phase 2 Target
Access token lifetime {{7 days (Bearer)15 minutes}}minutes (access) + 7 days (refresh)
Refresh token lifetime {{30Not days}}
Refresh token rotationimplemented Yes — single use, new token issuedRotate on each use
Session timeoutNever (background)token-based) App lock after {{5 minutes}}min inactivebackground
Max concurrent sessions {{Not limited3 devices}}devices max
Force logout triggers Manual logout onlyPassword change, suspicious activity, admin revocationrevoke

Token storage:storage (current Phase 1):

// CURRENT — lib/api.js — in-memory + AsyncStorage
// WARNING: AsyncStorage is not encrypted — acceptable for MVP, not for production
let token = null;  // Module-level in-memory
await AsyncStorage.setItem('auth_token', token);  // Persisted (unencrypted)

Token storage (Phase 2 target):

// PHASE 2 — lib/auth.ts
import * as SecureStore from 'expo-secure-store';

// CORRECT — encrypted secure storage
await SecureStore.setItemAsync('access_token', token, {
  keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});

// WRONG — neverDO useNOT AsyncStorageUSE orFOR MMKV for tokensTOKENS
// await AsyncStorage.setItem('access_token', token);  // DO NOT USEUnencrypted!

2.3 TokenBankID Storage (Secure Enclave)Authentication

BankID

isDrop'sprimarystrongmechanism.TheBankIDflowishandledtheBankIDOIDCproviderdoesstoreBankID
Platform Mechanism Accessauthentication Level
iOS Keychainby Services kSecAttrAccessibleWhenUnlockedThisDeviceOnly
Android AndroidDrop Keystore BIOMETRIC_STRONGnot see or DEVICE_CREDENTIAL
credentials.

CannotMobile beBankID backedflow up:(ADR-011):

Both
1. mechanismsGET /v1/auth/bankid/initiate?platform=mobile → { redirectUrl, state }
2. Open BankID in expo-web-browser (secure, isolated browser)
3. BankID redirects to drop://auth/callback?code=&state=
4. expo-linking catches the deep link
5. POST /v1/auth/bankid/callback → Bearer token (7-day)
6. Token stored (AsyncStorage Phase 1, expo-secure-store Phase 2)

Security properties of BankID:

  • Norwegian national ID system — eID Level 3 (substantial assurance)
  • All authentication happens in BankID's secure environment
  • Drop never sees BankID PIN or biometric data
  • BankID tokens are device-boundsingle-use and tokenstime-limited
  • do not transfer to new devices.


3. Data Protection

3.1 Secure Storage Policy

SQLite / MMKV
Data Type AllowedPhase 1 StoragePhase 2 Storage Forbidden Storage
AccessBearer token Keychain/KeystoreAsyncStorage (unencrypted) AsyncStorage,expo-secure-store MMKV,(Keychain/Keystore) Hardcoded
Refresh token Keychain/KeystoreNot implemented AsyncStorage, MMKV
Encryption keyexpo-secure-store Keychain/KeystoreAny other storageAsyncStorage
User PII Not cached locallySQLite (encrypted)encrypted with SQLCipher) AsyncStorage
Non-sensitive prefs AsyncStorage AsyncStorage
Biometric key hashNot implementedexpo-secure-storeAny other storage

Phase 1 risk: Bearer token stored in AsyncStorage (unencrypted) is the primary security gap. Mitigation: 7-day token expiry limits exposure window. Production apps should implement Phase 2 before launch.

3.2 Data Encryption at Rest

Phase 1: No local database. Auth token in AsyncStorage (unencrypted).

Phase 2 — Database encryption: {{SQLCipher for SQLite | Realm Encryption}}

// SQLCipherPhase initialization2 with— lib/database.ts
import * as SQLite from 'expo-sqlite';
import * as SecureStore from 'expo-secure-store';

async function openDatabase() {
  // Generate encryption key fromon Keystorefirst constlaunch
  encryptionKeylet dbKey = await SecureStore.getItemAsync('db_encryption_key');
  constif db(!dbKey) {
    dbKey = generateRandomKey(256);  // 256-bit random key
    await SecureStore.setItemAsync('db_encryption_key', dbKey);
  }

  return await SQLite.openDatabaseAsync('app.drop.db', { key: encryptionKey,dbKey });
}

Key generation: On first launch, generate 256-bit random key, store in Keychain/Keystore.

Encrypted fields (beyond DB encryption):

FieldEncryptionKey Source
Payment card (last 4 only stored)N/A — tokenizedStripe/Braintree
SSN / national IDAES-256-GCMKeychain/Keystore
Private messagesEnd-to-end (Signal protocol)Derived keys

3.3 Sensitive Data in Memory

Rule Phase 1 StatusPhase 2 Implementation
Clear passwords after use Partially (useState cleanup)Overwrite string variable, trigger GC
No sensitive dataPII in logsImplemented — no console.log(user) in production paths Custom logger strips PII fields
No sensitive dataPII in crash reportsTBD — no crash reporting in Phase 1 Sentry beforeSend hook scrubs payload
No sensitive dataPII in analytics MapTBD — no analytics in Phase 1Hash user ID → hashed ID before sendingIDs

3.4 Screenshot Prevention

Drop displays financial data (balance, transaction amounts). Screenshots should be prevented on sensitive screens.

// iOSPhase 2prevent screenshots programmatically
// (Note: React Native does NOT expose this API — native module required)

// Android — prevent screenshots andsensitive screen recording
import { Platform } from 'react-native';protection
import { preventScreenCapture, allowScreenCapture } from 'expo-screen-capture';

// Screens requiring screenshot prevention:
// - Dashboard (shows bank balance)
// - Send Money (shows account balance, transaction amounts)
// - Transaction History (financial data)

useEffect(() => {
  if (Platform.OS === 'android') {
    preventScreenCapture();
  }
  return () => allowScreenCapture();
}, []);

ScreensNote: requiringiOS screenshot prevention:

prevention
    via
  • expo-screen-capture Paymentshows /a card detailsblank screen
  • in Accountthe balanceapp switcher but cannot prevent all screenshots. Android FLAG_SECURE prevents screenshots and screen
  •  Personal document viewer
  •  {{OTHER_SENSITIVE_SCREEN}}

recording.

3.5 Clipboard Protection

  • Password fields: secureTextEntry={true} in React Native TextInput — disables clipboard by default on iOS
  • SensitiveTransaction dataamounts programmaticallycopied copied:to clipboard: clear clipboard after {{60 seconds}}seconds (Phase 2)
  • CustomAccount numbers: never copied to clipboard hook:by useSensitiveCopy(value,app clearAfterMs: 60000)shown as masked

4. Network Security

4.1 Certificate Pinning

Current (Phase 1): Not implemented. Standard HTTPS/TLS only.

Library:Phase 2 implementation: {{react-native-ssl-pinning | OkHttp certificate pinner (Android native) | URLSession (iOS native)}}

// UsingPhase 2 — requires react-native-ssl-pinning or OkHttp pinner
// Pinned endpoints:
// - drop-app.vercel.app (production API)
// - BankID OIDC endpoints

const response = await fetch('https://api.domain.com/endpoint'drop-app.vercel.app/api/transactions', {
  method: 'POST'GET',
  pkPinning:// true,Certificate sslPinning:pinning {configuration certs: ['api_cert_sha256_fingerprint'],
  },here
  headers: { /*Authorization: ...`Bearer */${token}` },
  body: JSON.stringify(payload),
});

Pinned endpoints:

  • api.{{domain.com}} — primary API
  • auth.{{domain.com}} — authentication

Certificate rotation process:

  1. Pin BOTHboth current cert and backup cert simultaneously
  2. Deploy app update with new cert added (OTA via EAS Update)
  3. After old cert expires, remove old cert from pins
  4. Test rotation in staging firstbefore production

Handling pin failures:

  • Log as security event to backend
  • Show user-facing error: "Secure connection failed"
  • Do NOT fall back to unpinned connection

4.2 SSL/TLS Configuration

Requirement Value
Minimum TLS version TLS 1.2
Preferred TLS version TLS 1.3
HTTP allowed No (ATSVercel enforcedenforces on iOS, enforce on Android)HTTPS)
Cipher suites Forward-secrecyTLS only1.3 default (ECDHE, DHE)forward-secrecy)

iOS App Transport Security (ATS):

  • No NSAllowsArbitraryLoads: true
  • All API calls go to https://drop-app.vercel.app (HTTPS enforced)

Android Network Security Config:

<!-- android/app/src/main/res/xml/network_security_config.xml — Phase 2 -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="false">
    <domain includeSubdomains="true">getdrop.no</domain>
    <domain includeSubdomains="true">drop-app.vercel.app</domain>
  </domain-config>
</network-security-config>

4.3 Network Request EncryptionSecurity

  • All requests over HTTPS (enforced viaby ATS + Android network security config)
  •  Sensitive payloads encrypted at application layer (in addition to TLS): {{Yes/No}}Vercel)
  • Request signing (HMAC) — not implemented (HMAC):Phase {{Yes/No}}2)
  •  Application-layer encryption for payment payloads — TBD (Phase 2)

5. Application Security

5.1 Code Obfuscation

Expo
Platform Tool Status
Android ProGuard / ProGuard/R8 (enabledvia byExpo defaultrelease in release)build {{Done/TODO}}Done (Expo default)
iOS Bitcode + Swift compiler optimizations {{Done/TODO}}Done (Expo default)
JavaScript Hermes bytecode compilation {{Done/TODO}}
JavaScriptDone (extra) {{metro-react-native-babel-preset obfuscation}}{{Done/TODO}}default)

ProGuard rules:Hermes: android/app/proguard-rules.proExpo TODO:SDK Review54 ProGuarduses rulesHermes by default. JavaScript is compiled to ensurebytecode, nomaking over-strippingreverse ofengineering reflection-dependentsignificantly code.harder than raw JS.


5.2 Root / Jailbreak Detection

Current (Phase 1): Not implemented.

Library:Phase 2 implementation: {{expo-device | react-native-jail-monkey | custom}}

// Phase 2 — lib/integrity.ts
import JailMonkey* as Device from 'jail-monkey'expo-device';

export function checkDeviceIntegrity(): SecurityResult{ compromised: boolean; reason: string } {
  const isJailbroken = JailMonkey.isJailBroken();
  const isDebuggedBuild = JailMonkey.isDebuggedMode();
  const onExternalStorage = JailMonkey.isOnExternalStorage();
  // Androidexpo-device provides basic device info
  // For more robust detection: react-native-device-info or jail-monkey
  const isEmulator = !Device.isDevice;

  return {
    compromised: isJailbrokenisEmulator ||&& isDebuggedBuild,!__DEV__,
    reason: isJailbrokenisEmulator ? 'jailbroken' : isDebuggedBuild ? 'debug'emulator_in_production' : 'clean',
  };
}

Response to compromised device:device (Phase 2):

  • {{Warn useruser: and"Drop allower ikke tilgjengelig på enheter med modifisert programvare"
  • Allow use with reducedwarning functionality(not |blocking Blockin accessPhase entirely2) | Loghigher silently}}blocking for payment flows
  • Justification:Log {{Explainsecurity decision}}event to backend

5.3 Tamper Detection

  •  App bundle signature verification on launch
  •  Resource hash verification for critical assets
  •  Backend validates app version — block known compromised versions

5.4 Debug Detection

// Detect if app is running under debugger in production
if (__DEV__ === false && detectDebugger()) {
  logSecurityEvent('debugger_attached');
  terminateSession();
}

Production builds must have:

  • __DEV__ mode disabled (Expo release build)
  • Console.console.log stripped (BabelExpo/Metro plugin:default transform-remove-console)in release builds)
  • Flipper disabled (Expo default in release builds)
  • Source maps NOT bundled with the app binary (upload to Sentry separately)separately (Phase 2)

5.54 Reverse Engineering Prevention

+ fetched from secure config endpoint
Measure ImplementationPhase 1Phase 2
No hardcoded secrets AllDone secrets— API URL only, no keysAPI keys fetched from vault at runtime orfrom injectedsecure via CIconfig
No plain API keys in bundle ObfuscatedDone Done
Sensitive logic on server BusinessDone logic requiringDrop securityis stayspass-through; backend-payments initiated server-sideDone
Binary protection Hermes bytecode (done)ProGuard /rules R8 (Android), Bitcode stripping (iOS)review

TODO: Run MobSF static analysis before each major release and review findings.


6. OWASP Mobile Top 10 Checklist

# Risk Phase 1 Status Notes
M1 Improper Credential Usage {{Pass/Fail/N/A}}Partial TokensBearer token in Keychain,AsyncStorage no(unencrypted) hardcoded credsPhase 2 fix
M2 Inadequate Supply Chain Security {{Pass/Fail}}Unknown Dependency audit npm audit not run in CI yet
M3 Insecure Authentication/Authorization {{Pass/Fail}}Pass BankID + JWT validated server-side
M4 Insufficient Input/Output Validation {{Pass/Fail}}Pass ZodReact validationNative onTextInput allhandles inputsbasic validation; XSS not applicable to React Native
M5 Insecure Communication {{Pass/Fail}}Pass TLSHTTPS +enforced; cert pinning Phase 2
M6 Inadequate Privacy Controls {{Pass/Fail}}Partial Data minimization,minimization done; GDPR consent in web app — mobile privacy notice TBD
M7 Insufficient Binary Protections {{Pass/Fail}}Pass Obfuscation,Hermes nobytecode; debugExpo release build in prod
M8 Security Misconfiguration {{Pass/Fail}}Pass No dev configs in prod buildbuild; __DEV__ disabled
M9 Insecure Data Storage {{Pass/Fail}}Fail Keychain/KeystoreBearer +token encryptedin DBunencrypted AsyncStorage — fix before production
M10 Insufficient Cryptography {{Pass/Fail}}Pass AES-256,TLS SHA-256,1.3; no MD5/SHA-1 usage

Critical action before production: M9 (Insecure Data Storage) — migrate Bearer token from AsyncStorage to expo-secure-store. This is the single most important security fix before v1.0 production launch.


7. Security Testing Tools

Tool Type When Output
MobSF (Mobile Security Framework) Static analysis Pre-release Risk report
Fridanpm audit / pnpm audit DynamicDependency analysisscan PenetrationEvery testPR RuntimeCVE behaviorreport
ObjectionExpo Security Checks Runtime analysisBuild-time PenetrationEAS testBuild BypassSecurity checkswarnings
Burp Suite Network proxy Penetration test API vulnerabilities
FridaDynamic analysisPenetration testRuntime behavior
OWASP ZAP Automated API scan CI/CD Vulnerability report
npm audit / yarn auditDependency scanEvery PRCVE report

Penetration test cadence: {{Annual | Before eachv1.0 majorproduction releaselaunch |(pre-launch), Quarterly}}then annually. Last pentest date: {{DATE}}Not yet conducted. Next pentest date: {{DATE}}Before production launch.


8. Compliance Requirements

NotconsentPCI-compliant SDK (Stripe/Braintree) —StorePlaywith
Requirement Applicable StatusNotes
GDPR (EUEU/EEA users) {{Yes/No}}Yes UserPartialPrivacy policy at /privacy; data deletion,deletion exportvia endpointssupport; consent management in web app
PSD2 (Payment Services Directive 2)YesPassPISP/AISP model; Open Banking; pre-payment disclosure implemented
Finanstilsynet (Norwegian FSA)YesIn progressRegulatory compliance ongoing
Hvitvaskingsloven (AML)YesPass5-year data retention; KYC via BankID
CCPA (California users) {{Yes/No}}No DoN/A Norwegian Sellmarket optiononly — no California users targeted
COPPA (under 13) {{Yes/No}}No ParentalN/A 18+ flowminimum age (BankID requirement)
PCI DSS (payment data) {{Yes/No}}No UseN/A Drop never storestores card data — pass-through model
App Store privacyPrivacy labelLabel Yes AppTODO Must Connectcomplete privacybefore questionnairesubmission
Play Store dataData safetySafety Yes GoogleTODO Must Consolecomplete databefore safety formsubmission
HIPAA (health data) {{Yes/No}}No BAAN/A No vendors,health encryption at rest+transitdata

9. Security Roadmap

Phase 1 (Current — MVP)

  •  HTTPS/TLS enforced
  •  BankID authentication via expo-web-browser
  •  Hermes bytecode (basic obfuscation)
  •  CRITICAL: Migrate Bearer token to expo-secure-store (before production)

Phase 2 (Pre-Launch)

  •  expo-secure-store for all tokens
  •  Certificate pinning for API endpoints
  •  Biometric authentication (Face ID / Touch ID)
  •  Screenshot prevention on financial screens
  •  Root/jailbreak detection with user warning
  •  Mobile penetration test (MobSF + Burp Suite)
  •  Source maps configured (Sentry, not bundled)

Phase 3 (Post-Launch)

  •  App attestation (Play Integrity API / App Attest)
  •  Transaction anomaly detection
  •  Session device binding
  •  Refresh token rotation
  •  Self-service account deletion (GDPR)

Approval

Role Name Date Signature
Author John (AI Director) 2026-02-23
Mobile Lead
Security Lead
Legal