Back to Blog

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

Viprasol Tech Team
April 28, 2026
12 min read

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 audit in 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

ScopeInvestment
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 →


Share this article:

About the Author

V

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.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

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

Viprasol · Web Development

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.