Mobile App Security: OWASP Mobile Top 10 and How to Address Each
Mobile app security in 2026 — OWASP Mobile Top 10, certificate pinning, secure storage, root/jailbreak detection, obfuscation, and production patterns for iOS a
Mobile App Security: OWASP Mobile Top 10 and How to Address Each
Mobile apps have a larger attack surface than web apps. The app runs on a device the attacker controls, network traffic can be intercepted, secrets can be extracted from the binary, and storage can be read if the device is rooted.
The OWASP Mobile Application Security Verification Standard (MASVS) defines the security baseline. This guide covers the most important issues and practical mitigations.
OWASP Mobile Top 10 (2024)
M1: Improper Credential Usage
Hardcoded API keys, credentials, or secrets in the app binary or source code.
// ❌ BAD: API key in source code
const API_KEY = 'sk-live-abc123def456'; // Extractable from binary
// ❌ BAD: In .env file committed to repo
// STRIPE_SECRET_KEY=sk_live_abc123
// ✅ GOOD: Secrets never in client — all secret API calls via your backend
// Mobile app calls YOUR API, which calls Stripe with the server-side key
// The mobile app only ever holds a session token for your API
What to put in mobile apps: Public keys only (publishable API keys, public endpoints). Never secret keys, never admin tokens.
M2: Inadequate Supply Chain Security
Third-party libraries with vulnerabilities or malicious packages.
# React Native: audit dependencies
npm audit
npx expo-doctor # For Expo projects
# iOS: Check for known vulnerabilities
pod audit # CocoaPods (if available for your dependencies)
# Android: Gradle dependency audit
./gradlew dependencyUpdates
Best practices:
- Lock dependency versions exactly (
package-lock.json,Podfile.lock) - Review new dependency additions in code review
- Run
npm auditin CI pipeline — fail on high/critical
M3: Insecure Authentication and Authorization
Weak authentication, improper token storage, missing token validation.
// React Native: Secure token storage
import * as SecureStore from 'expo-secure-store';
import * as Keychain from 'react-native-keychain';
// ❌ BAD: Tokens in AsyncStorage (unencrypted, accessible to other apps on rooted device)
await AsyncStorage.setItem('authToken', token);
// ✅ GOOD: Keychain (iOS) / Keystore (Android) — hardware-backed encryption
await SecureStore.setItemAsync('authToken', token, {
requireAuthentication: true, // Require biometrics/PIN to access
});
// Or with react-native-keychain (more options):
await Keychain.setGenericPassword('token', authToken, {
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
M4: Insufficient Input/Output Validation
Trusting data received from the server or user input without validation.
// Validate API responses on the client too
import { z } from 'zod';
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
plan: z.enum(['free', 'pro', 'enterprise']),
// Explicitly omit any sensitive fields the server might send
});
async function fetchUserProfile(userId: string) {
const response = await apiClient.get(`/users/${userId}`);
// Validate and strip unknown fields
const user = UserSchema.parse(response.data);
return user;
}
M5: Insecure Communication
Transmitting sensitive data over unencrypted channels or accepting invalid TLS certificates.
<!-- Android: Network Security Config — disable cleartext traffic -->
<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- Block all cleartext (HTTP) traffic -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<!-- For debug builds only: allow cleartext to localhost -->
<debug-overrides>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</debug-overrides>
</network-security-config>
<!-- AndroidManifest.xml -->
<application
android:networkSecurityConfig="@xml/network_security_config"
...>
<!-- iOS: ATS (App Transport Security) — Info.plist -->
<!-- ATS is enabled by default in iOS. Don't disable it. -->
<!-- If you MUST allow HTTP for specific domains: -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>staging.yourapi.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
M6: Inadequate Privacy Controls
Collecting more data than needed, storing PII insecurely, leaking data in logs.
// ❌ BAD: Log sensitive data
console.log(`User logged in: ${email}, password: ${password}`);
console.log(`Payment card: ${cardNumber}`);
// ✅ GOOD: Never log sensitive fields
console.log(`User logged in: userId=${userId}`);
// Mask sensitive values in error reporting
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: process.env.SENTRY_DSN,
beforeSend(event) {
// Strip sensitive data from Sentry events
if (event.request?.data) {
const sensitiveFields = ['password', 'cardNumber', 'cvv', 'ssn'];
for (const field of sensitiveFields) {
if (event.request.data[field]) {
event.request.data[field] = '[REDACTED]';
}
}
}
return event;
},
});
M7: Insufficient Binary Protections
App binary can be reverse-engineered, repackaged, or modified.
Code obfuscation (React Native / JavaScript):
// metro.config.js — enable minification
module.exports = {
transformer: {
minifierConfig: {
mangle: { toplevel: true }, // Rename top-level variables
output: { ascii_only: true }, // ASCII-only output
compress: {
drop_console: true, // Remove console.log in production
},
},
},
};
Android ProGuard/R8:
# android/app/proguard-rules.pro
-keep class com.yourapp.** { *; }
-keepattributes Signature
-keepattributes *Annotation*
# R8 is enabled by default in release builds
Root/Jailbreak detection:
import JailMonkey from 'jail-monkey';
async function checkDeviceIntegrity(): Promise<boolean> {
if (__DEV__) return true; // Skip in development
const isJailbroken = JailMonkey.isJailBroken();
const isOnExternalStorage = await JailMonkey.isOnExternalStorage();
const isDeveloperMode = JailMonkey.isDeveloperMode();
if (isJailbroken || isOnExternalStorage) {
Alert.alert(
'Security Warning',
'This app cannot run on a jailbroken/rooted device.',
[{ text: 'Exit', onPress: () => BackHandler.exitApp() }]
);
return false;
}
return true;
}
M8: Security Misconfiguration
Exposed debug endpoints, overly permissive Android permissions, unused iOS entitlements.
<!-- AndroidManifest.xml: Request minimum permissions -->
<!-- ❌ BAD: Requesting READ_CONTACTS when you don't need it -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- ✅ GOOD: Only permissions your app genuinely needs -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- Request at runtime only when the feature is used -->
// React Native: Request permissions at the right moment (not on startup)
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';
async function requestCameraPermission(): Promise<boolean> {
const result = await request(
Platform.OS === 'ios'
? PERMISSIONS.IOS.CAMERA
: PERMISSIONS.ANDROID.CAMERA,
{
title: 'Camera Permission',
message: 'We need camera access to scan QR codes.',
buttonPositive: 'Allow',
buttonNegative: 'Deny',
}
);
return result === RESULTS.GRANTED;
}
M9: Insecure Data Storage
Sensitive data stored in unencrypted local storage, logs, or accessible locations.
// ❌ BAD: Sensitive data in AsyncStorage (plaintext SQLite)
await AsyncStorage.setItem('userEmail', email);
await AsyncStorage.setItem('sessionToken', token);
// ✅ GOOD: Encrypted storage for sensitive data
import EncryptedStorage from 'react-native-encrypted-storage';
await EncryptedStorage.setItem('session', JSON.stringify({
token,
userId,
expiresAt: Date.now() + 86400000,
}));
// For non-sensitive data (display preferences, cached content):
// AsyncStorage is fine
await AsyncStorage.setItem('theme', 'dark');
await AsyncStorage.setItem('language', 'en');
M10: Insufficient Cryptography
Using weak algorithms (MD5, SHA-1), improper key management, custom crypto.
// ❌ BAD: Implementing your own crypto (always wrong)
function encryptPassword(password: string): string {
// Don't do this
return Buffer.from(password).toString('base64');
}
// ✅ GOOD: Use platform crypto APIs
import { randomBytes, createHmac } from 'react-native-quick-crypto';
// For verifying data integrity
function generateHmac(data: string, secret: string): string {
return createHmac('sha256', secret).update(data).digest('hex');
}
// Generate cryptographic random values
function generateToken(length: number = 32): string {
return randomBytes(length).toString('hex');
}
Certificate Pinning
Prevent MITM attacks by pinning your server's TLS certificate or public key:
// React Native with react-native-ssl-pinning
import { fetch } from 'react-native-ssl-pinning';
const response = await fetch('https://api.yourapp.com/endpoint', {
method: 'POST',
sslPinning: {
certs: ['sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='],
// SHA-256 fingerprint of your server's certificate
},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
// The request will fail if the server's certificate doesn't match
// — prevents MITM even with a custom root CA installed
Warning: Certificate pinning requires a rotation strategy. When your TLS certificate expires, you must update the app before the cert expires or users will be locked out.
🌐 Looking for a Dev Team That Actually Delivers?
Most agencies sell you a project manager and assign juniors. Viprasol is different — senior engineers only, direct Slack access, and a 5.0★ Upwork record across 100+ projects.
- React, Next.js, Node.js, TypeScript — production-grade stack
- Fixed-price contracts — no surprise invoices
- Full source code ownership from day one
- 90-day post-launch support included
Security Testing Checklist
## Pre-Release Security Checklist
### Static Analysis
- [ ] No secrets/keys in source code or config files
- [ ] `npm audit` / `pod audit` passes (no high/critical)
- [ ] ProGuard/R8 enabled for Android release builds
- [ ] Debug logging disabled in release builds
### Network Security
- [ ] All API calls use HTTPS
- [ ] Certificate pinning implemented for production API
- [ ] ATS/NSC configured correctly
### Data Storage
- [ ] Sensitive data in Keychain/Keystore (not AsyncStorage)
- [ ] No sensitive data in logs
- [ ] Temporary files containing sensitive data are deleted
### Authentication
- [ ] Biometric/PIN required for sensitive operations
- [ ] Session tokens expire appropriately
- [ ] Refresh tokens rotated after use
### Device Security
- [ ] Root/jailbreak detection (if required by threat model)
- [ ] Minimum OS version enforced (old OS = unpatched vulnerabilities)
🚀 Senior Engineers. No Junior Handoffs. Ever.
You get the senior developer, not a project manager who relays your requirements to someone you never meet. Every Viprasol project has a senior lead from kickoff to launch.
- MVPs in 4–8 weeks, full platforms in 3–5 months
- Lighthouse 90+ performance scores standard
- Works across US, UK, AU timezones
- Free 30-min architecture review, no commitment
Security Audit Cost
| Scope | Investment |
|---|---|
| OWASP Mobile checklist review | $3,000–$8,000 |
| Static analysis + basic remediation | $5,000–$15,000 |
| Full penetration test (iOS or Android) | $8,000–$25,000 |
| Both platforms + backend API | $20,000–$50,000 |
Working With Viprasol
We build and audit mobile applications with security built in from the start — secure storage, certificate pinning, OWASP compliance, and penetration test remediation.
→ Mobile security review →
→ Mobile App Development →
→ API Security Best Practices →
About the Author
Viprasol Tech Team
Custom Software Development Specialists
The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.
Need a Modern Web Application?
From landing pages to complex SaaS platforms — we build it all with Next.js and React.
Free consultation • No commitment • Response within 24 hours
Need a custom web application built?
We build React and Next.js web applications with Lighthouse ≥90 scores, mobile-first design, and full source code ownership. Senior engineers only — from architecture through deployment.