Skip to main content
React Native Security Reference: OWASP MAS Implementation for Cross-Platform AppsStandards
5 min readFor Developers

React Native Security Reference: OWASP MAS Implementation for Cross-Platform Apps

Scope - What This Guide Covers

This guide focuses on implementing security for React Native applications using resources from the OWASP Mobile Application Security (MAS) project. It provides specific guidance for the JavaScript bridge layer, native module interfaces, and dependency chains that distinguish React Native from traditional native iOS or Android development.

What's included:

  • OWASP MAS verification requirements for React Native
  • JavaScript engine-specific threat vectors (JavaScriptCore vs. Hermes)
  • Dependency management controls for npm packages and native modules
  • Platform-specific implementation gaps

What's excluded:

  • General mobile app security (covered in OWASP Mobile Top 10)
  • Backend API security
  • Network layer controls

Key Concepts and Definitions

JavaScript Bridge: The communication layer between your JavaScript code and native platform APIs. Each call across this bridge is a potential injection or data exposure point.

Hermes Engine: Meta's JavaScript engine optimized for React Native. Unlike the default JavaScriptCore engine on iOS or V8 on Android, Hermes uses bytecode compilation, altering your threat model for code tampering and reverse engineering.

Native Modules: Platform-specific code (Swift/Objective-C or Java/Kotlin) that extends React Native's capabilities. These modules bypass React Native's abstraction layer and require separate security review.

Metro Bundler: The build tool that packages your JavaScript code. Your security controls must account for what happens during bundling—source maps, debugging symbols, and environment variables all affect your attack surface.

Requirements Breakdown

OWASP MASVS Storage Requirements (MASVS-STORAGE)

MASVS-STORAGE-1: Protect sensitive data at rest using platform encryption APIs.

React Native specifics:

  • Never store tokens or keys in AsyncStorage without encryption
  • Use react-native-keychain or expo-secure-store for iOS Keychain and Android Keystore access
  • Validate that your chosen library calls native secure storage—some claim to but use encrypted SharedPreferences instead

MASVS-STORAGE-2: Prevent sensitive data leakage through system services.

React Native specifics:

  • Disable screenshot capture for sensitive screens using react-native-screen-capture
  • Clear navigation state containing sensitive data before backgrounding
  • The JavaScript bridge serializes all data—assume anything passed across it could appear in crash logs

OWASP MASVS Crypto Requirements (MASVS-CRYPTO)

MASVS-CRYPTO-1: Use strong, standard cryptographic algorithms.

React Native specifics:

  • Avoid JavaScript crypto libraries for sensitive operations—they're slow and leak timing information
  • Bridge to native crypto APIs using libraries that wrap CommonCrypto (iOS) or AndroidKeyStore (Android)
  • Hermes bytecode is not encryption—treat your bundle as plaintext

OWASP MASVS Code Requirements (MASVS-CODE)

MASVS-CODE-4: Validate all input from untrusted sources.

React Native specifics:

  • Validate every prop passed to a native module on the native side
  • Deep link handlers are risky—React Navigation's linking config doesn't sanitize by default
  • WebView components (especially with injectedJavaScript) create XSS opportunities

Implementation Guidance

JavaScript Engine Selection

You run JavaScript on either the platform's default engine or Hermes. Each has different security implications:

Using Hermes:

  1. Enable in android/gradle.properties: hermesEnabled=true
  2. Enable in ios/Podfile: Add hermes_enabled flag
  3. Test bytecode tampering controls—Hermes bundles are easier to modify than native code
  4. Monitor CVEs specific to Hermes (separate from JavaScriptCore vulnerabilities)

Staying with default engines:

  • iOS uses JavaScriptCore (same engine as Safari)
  • Android uses V8 or JavaScriptCore depending on version
  • You inherit platform security updates automatically
  • Debugging tools work more reliably

Dependency Chain Security

Your React Native app has three dependency layers:

Layer 1: npm packages (JavaScript)

  • Run npm audit in CI—but it only catches known CVEs
  • Use npm ls to map your actual dependency tree—transitive dependencies outnumber direct ones 10:1
  • Pin versions in package-lock.json and review updates manually
  • Particular risk: packages that include native code or require linking

Layer 2: Native modules (CocoaPods/Gradle)

  • iOS: Check ios/Podfile.lock for native dependencies
  • Android: Review android/app/build.gradle for Maven packages
  • These bypass npm audit entirely—you need separate tooling
  • Many React Native libraries bundle outdated native SDKs

Layer 3: Platform SDKs

  • Set minimum iOS version to receive security updates (currently iOS 15+)
  • Set minSdkVersion in Android to API level 23+ minimum
  • Each platform version you support expands your threat surface

Code Integrity Controls

For development builds:

  1. Enable Metro bundler source maps for debugging
  2. Use __DEV__ flag to include verbose logging
  3. Accept that these builds are fully reversible

For production builds:

  1. Strip source maps: Set bundleInRelease: false in build config
  2. Enable ProGuard/R8 on Android native code
  3. Enable bitcode on iOS (deprecated in Xcode 14, but use alternatives)
  4. Consider code obfuscation for JavaScript—tools like javascript-obfuscator add friction but aren't cryptographic protection

Reality check: React Native apps are easier to reverse engineer than fully native apps. Your security model must assume attackers can read your client-side code.

Common Pitfalls

Pitfall 1: Treating AsyncStorage as secure AsyncStorage is plaintext storage. Wrapping it with encryption at the JavaScript layer doesn't help—the keys are still in your bundle.

Pitfall 2: Trusting bridge data Any data that crosses the JavaScript-to-native bridge can be intercepted or modified on jailbroken/rooted devices. Validate on the native side.

Pitfall 3: Ignoring native module security When you npm install react-native-something, you're trusting that package's native code. Review the Objective-C/Swift and Java/Kotlin code, not just the JavaScript wrapper.

Pitfall 4: Using WebView without restrictions If you render untrusted content in a WebView with javaScriptEnabled={true}, you've created an XSS vector. Set originWhitelist, disable JavaScript for untrusted content, and never use injectedJavaScript with user input.

Pitfall 5: Skipping certificate pinning React Native's networking uses platform APIs, but you must explicitly configure pinning. Use react-native-ssl-pinning or implement native pinning and bridge to it.

Quick Reference Table

Security Control MASVS Requirement React Native Implementation Verification Method
Secure storage MASVS-STORAGE-1 react-native-keychain for tokens/keys Check native code calls Keychain/Keystore APIs
Input validation MASVS-CODE-4 Validate in native modules, not JavaScript Review bridge interface code
Certificate pinning MASVS-NETWORK-1 react-native-ssl-pinning or native implementation Test with proxy (should fail)
Code obfuscation MASVS-RESILIENCE-4 JavaScript obfuscation + ProGuard/R8 Decompile production bundle
Screenshot prevention MASVS-STORAGE-2 react-native-screen-capture Attempt screenshot on sensitive screen
Dependency scanning MASVS-CODE-3 npm audit + native dependency tools Check CI pipeline output
Deep link validation MASVS-CODE-4 Sanitize in linking config + native validation Fuzz test with malformed URLs
WebView restrictions MASVS-CODE-4 Set originWhitelist, disable JS for untrusted content Review WebView props in code

Testing resources:

When you implement these controls, document which MASVS requirements you're addressing. Your next audit will ask for this mapping.

Topics:Standards

You Might Also Like