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-keychainorexpo-secure-storefor 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:
- Enable in
android/gradle.properties:hermesEnabled=true - Enable in
ios/Podfile: Addhermes_enabledflag - Test bytecode tampering controls—Hermes bundles are easier to modify than native code
- 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 auditin CI—but it only catches known CVEs - Use
npm lsto 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.lockfor native dependencies - Android: Review
android/app/build.gradlefor 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
minSdkVersionin Android to API level 23+ minimum - Each platform version you support expands your threat surface
Code Integrity Controls
For development builds:
- Enable Metro bundler source maps for debugging
- Use
__DEV__flag to include verbose logging - Accept that these builds are fully reversible
For production builds:
- Strip source maps: Set
bundleInRelease: falsein build config - Enable ProGuard/R8 on Android native code
- Enable bitcode on iOS (deprecated in Xcode 14, but use alternatives)
- Consider code obfuscation for JavaScript—tools like
javascript-obfuscatoradd 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:
- OWASP Mobile Application Security Testing Guide (MASTG) provides test cases
- Use Frida to test JavaScript bridge security on jailbroken/rooted devices
- MobSF automates some static analysis for React Native apps
When you implement these controls, document which MASVS requirements you're addressing. Your next audit will ask for this mapping.



