{"id":100010,"date":"2026-10-09T00:47:43","date_gmt":"2026-10-09T00:47:43","guid":{"rendered":"http:\/\/nandee-post-5"},"modified":"2026-10-09T00:47:43","modified_gmt":"2026-10-09T00:47:43","slug":"owasp-mobile-top-10","status":"publish","type":"post","link":"https:\/\/www.nandee.ai\/blog\/owasp-mobile-top-10\/","title":{"rendered":"OWASP Mobile Top 10 (2024): A Complete Guide for Developers"},"content":{"rendered":"<p>Most mobile breaches do not start with a clever zero-day. They start with someone downloading your app, unzipping it, and reading what you shipped: an API key in a constants file, a debug flag left on, a token sitting in plain storage. Code obfuscation renames classes and methods, but the strings an attacker wants usually survive it. The 2024 Top 10 is built around exactly these everyday mistakes.<\/p>\n<p>The OWASP Mobile Top 10 (2024) is the ten most critical security risks in Android and iOS apps, and it is the first full update since 2016. If you ship a mobile app, this list is the fastest way to know where attackers look first and what your pentest report will flag.<\/p>\n<p>This article covers each risk from M1 to M10 with a realistic example, the fix, and how to test for it. It is written for mobile developers, security engineers, and startup CTOs who need a working checklist, not a theory lecture.<\/p>\n<p><strong>Quick answer: the 2024 list<\/strong><\/p>\n<ol>\n<li>\n<p>M1: Improper Credential Usage<\/p>\n<\/li>\n<li>\n<p>M2: Inadequate Supply Chain Security<\/p>\n<\/li>\n<li>\n<p>M3: Insecure Authentication\/Authorization<\/p>\n<\/li>\n<li>\n<p>M4: Insufficient Input\/Output Validation<\/p>\n<\/li>\n<li>\n<p>M5: Insecure Communication<\/p>\n<\/li>\n<li>\n<p>M6: Inadequate Privacy Controls<\/p>\n<\/li>\n<li>\n<p>M7: Insufficient Binary Protections<\/p>\n<\/li>\n<li>\n<p>M8: Security Misconfiguration<\/p>\n<\/li>\n<li>\n<p>M9: Insecure Data Storage<\/p>\n<\/li>\n<li>\n<p>M10: Insufficient Cryptography<\/p>\n<\/li>\n<\/ol>\n<p><strong>What changed from 2016 to 2024<\/strong><\/p>\n<p>Four categories are new, four old ones merged into two, and the rest moved or were renamed. The biggest shift: credentials and the supply chain now sit at the top, while insecure data storage fell from M2 to M9.<\/p>\n<table>\n<colgroup>\n<col>\n<col>\n<col><\/colgroup>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>2024 category<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Came from (2016)<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Change<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M1 Improper Credential Usage<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>None<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>New<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M2 Inadequate Supply Chain Security<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>None<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>New<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M3 Insecure Authentication\/Authorization<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M4 Insecure Authentication + M6 Insecure Authorization<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Merged<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M4 Insufficient Input\/Output Validation<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>None<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>New<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M5 Insecure Communication<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M3 Insecure Communication<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Moved down<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M6 Inadequate Privacy Controls<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>None<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>New<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M7 Insufficient Binary Protections<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M8 Code Tampering + M9 Reverse Engineering<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Merged<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M8 Security Misconfiguration<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M10 Extraneous Functionality<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Renamed, moved up<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M9 Insecure Data Storage<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M2 Insecure Data Storage<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Moved down<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M10 Insufficient Cryptography<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>M5 Insufficient Cryptography<\/p>\n<\/td>\n<td colspan=\"1\" rowspan=\"1\">\n<p>Moved down<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A lower rank does not mean a risk got safer. It means attackers now get more value from stolen keys, poisoned SDKs, and broken backends than from reading a local database. OWASP also listed data leakage, hardcoded secrets, unprotected endpoints, and unsafe sharing as near misses for the list (<a target=\"_blank\" href=\"https:\/\/owasp.github.io\/www-project-mobile-top-10\/2023-risks\/\" rel=\"noopener noreferrer\"><span style=\"color: rgb(79, 129, 189)\">OWASP Mobile Top 10 2024<\/span><\/a>, <a target=\"_blank\" href=\"https:\/\/cybersecurity.bureauveritas.com\/services\/information-technology\/pentesting-services\/what-can-be-pentested\/mobile-apps-pentesting\/owasp-mobile-top-ten-update-2024\" rel=\"noopener noreferrer\"><span style=\"color: rgb(79, 129, 189)\">Bureau Veritas<\/span><\/a>).<\/p>\n<p><strong>The 10 risks, one by one<\/strong><\/p>\n<p>Each risk below follows the same pattern: what it is, what it looks like in a real app, how to fix it, and how to test for it. Every section ends with the deeper article in this series.<\/p>\n<p><strong>M1: Improper Credential Usage<\/strong><\/p>\n<p><strong>What it is.<\/strong> Hardcoded API keys, passwords, or tokens inside the app, or credentials sent and stored carelessly. Anything in the binary is public: an APK unzips in seconds and an IPA is one decrypt away.<\/p>\n<p><strong>Example.<\/strong> A fintech app ships a live payment provider secret key in its Kotlin code so the app can create charges directly.<\/p>\n<p><em>\/\/ Bad: anyone with jadx can read this<\/em><span style=\"color: rgb(0, 0, 0)\"><br \/><\/span><span style=\"color: rgb(0, 112, 32)\"><strong>private<\/strong><\/span><span style=\"color: rgb(0, 0, 0)\"> <\/span><span style=\"color: rgb(0, 112, 32)\"><strong>const<\/strong><\/span><span style=\"color: rgb(0, 0, 0)\"> <\/span><span style=\"color: rgb(0, 112, 32)\"><strong>val<\/strong><\/span><span style=\"color: rgb(0, 0, 0)\"> <\/span><span style=\"color: rgb(25, 23, 124)\">PAYMENTS_KEY<\/span><span style=\"color: rgb(0, 0, 0)\"> <\/span><span style=\"color: rgb(102, 102, 102)\">=<\/span><span style=\"color: rgb(0, 0, 0)\"> <\/span><span style=\"color: rgb(64, 112, 160)\">&#8220;sk_live_&#8230;&#8221;<\/span><\/p>\n<p><strong>Fix.<\/strong> Keep secrets on your server. The app authenticates the user, then asks your backend for a short-lived, narrowly scoped token. Store user session tokens in Android Keystore or iOS Keychain, never in plain preferences. Rotate any key that has ever shipped in a build.<\/p>\n<p><strong>Test.<\/strong> Decompile with jadx or apktool and search for key patterns (sk_live, AIza, AKIA, Bearer). Run strings on the iOS binary. Add a secret scanner to CI so a key never reaches a release.<\/p>\n<p><strong>Go deeper:<\/strong> M1 Improper Credential Usage: Examples and Fixes; Hardcoded API Keys in Mobile Apps: How Attackers Find Them.<\/p>\n<p><strong>M2: Inadequate Supply Chain Security<\/strong><\/p>\n<p><strong>What it is.<\/strong> Risk that enters through code you did not write: third-party SDKs, open-source packages, build tools, and CI pipelines. The typical mobile app ships dozens of SDKs, and each one runs with your app\u2019s permissions.<\/p>\n<p><strong>Example.<\/strong> An ad or analytics SDK update starts collecting device data your privacy policy never mentions, or a compromised package lands in your build through an unpinned version range.<\/p>\n<p><strong>Fix.<\/strong> Pin dependency versions and commit lockfiles. Generate an SBOM for every release. Review what each SDK collects and which permissions it adds to your merged manifest. Protect signing keys and CI secrets as carefully as production credentials.<\/p>\n<p><strong>Test.<\/strong> Diff the merged AndroidManifest.xml and the iOS privacy manifests between releases. Run software composition analysis (SCA) on Gradle, CocoaPods, Swift Package Manager, npm, and pub dependencies.<\/p>\n<p><strong>Go deeper:<\/strong> M2 Inadequate Supply Chain Security in Mobile Apps; Third-Party SDK Risk: Auditing What You Ship; Mobile SBOM: Why You Need One and How to Generate It.<\/p>\n<p><strong>M3: Insecure Authentication\/Authorization<\/strong><\/p>\n<p><strong>What it is.<\/strong> Weak login flows and, more often, authorization decisions made on the device instead of the server. If the app decides who is an admin, an attacker with Frida decides too.<\/p>\n<p><strong>Example.<\/strong> The app hides a premium screen unless user.isPremium == true, and the API behind that screen never checks the flag. Changing one boolean at runtime unlocks paid features. A related pattern: an endpoint like \/api\/orders\/{id} returns any user\u2019s order when you change the ID (BOLA\/IDOR).<\/p>\n<p><strong>Fix.<\/strong> Enforce every authorization decision on the server, on every request. Use OAuth 2.0 with PKCE and the system browser for sign-in. Bind sessions to short-lived tokens with server-side revocation.<\/p>\n<p><strong>Test.<\/strong> Intercept traffic with a proxy, then replay requests with another user\u2019s IDs and with a lower-privilege token. Hook client checks with Frida to confirm the server still refuses.<\/p>\n<p><strong>Go deeper:<\/strong> Securing OAuth and PKCE in Mobile Apps; JWT Handling Mistakes in Mobile Apps; BOLA\/IDOR Vulnerabilities in Mobile Backends.<\/p>\n<p><strong>M4: Insufficient Input\/Output Validation<\/strong><\/p>\n<p><strong>What it is.<\/strong> The app trusts data from deep links, intents, WebViews, QR codes, clipboard, files, or its own backend. Mobile apps have far more input channels than most developers track.<\/p>\n<p><strong>Example.<\/strong> A deep link myapp:\/\/open?url= loads any URL into a WebView that has JavaScript and a native bridge enabled. An attacker sends a link and gets code running with access to the bridge.<\/p>\n<p><strong>Fix.<\/strong> Validate every external input against an allowlist. Use parameterized queries in SQLite and content providers. Restrict WebViews to trusted origins and avoid exposing native interfaces to untrusted content.<\/p>\n<p><strong>Test.<\/strong> Enumerate entry points from the manifest and Info.plist, then fuzz each one with malformed and malicious values using adb, Drozer, or crafted links.<\/p>\n<p><strong>Go deeper:<\/strong> Android Intent Vulnerabilities and How to Prevent Them; WebView Security in Android: 7 Common Mistakes; Deep Link and App Link Hijacking on Android.<\/p>\n<p><strong>M5: Insecure Communication<\/strong><\/p>\n<p><strong>What it is.<\/strong> Data sent in cleartext, TLS validation turned off, or trust decisions that let an attacker on the same Wi-Fi read and modify traffic.<\/p>\n<p><strong>Example.<\/strong> A developer adds a custom TrustManager that accepts every certificate to get past a staging error, and it ships to production.<\/p>\n<p><strong>Fix.<\/strong> Never override certificate validation. Block cleartext traffic and, for high-risk apps, pin your API\u2019s public keys with a backup pin.<\/p>\n<p><span style=\"color: rgb(96, 160, 176)\"><em>&lt;!&#8211; res\/xml\/network_security_config.xml &#8211;&gt;<\/em><\/span><span style=\"color: rgb(0, 0, 0)\"><br \/>&lt;<\/span><strong>network-security-config<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&nbsp; &nbsp; &lt;<\/span><strong>base-config<\/strong> cleartextTrafficPermitted=<span style=\"color: rgb(64, 112, 160)\">&#8220;false&#8221;<\/span><span style=\"color: rgb(0, 0, 0)\"> \/&gt;<br \/>&nbsp; &nbsp; &lt;<\/span><strong>domain-config<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&nbsp; &nbsp; &nbsp; &nbsp; &lt;<\/span><strong>domain<\/strong> includeSubdomains=<span style=\"color: rgb(64, 112, 160)\">&#8220;true&#8221;<\/span><span style=\"color: rgb(0, 0, 0)\">&gt;<\/span><a target=\"_blank\" href=\"http:\/\/api.example.com\" rel=\"noopener noreferrer\"><span style=\"color: rgb(0, 0, 0)\">api.example.com<\/span><\/a><span style=\"color: rgb(0, 0, 0)\">&lt;\/<\/span><strong>domain<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&nbsp; &nbsp; &nbsp; &nbsp; &lt;<\/span><strong>pin-set<\/strong> expiration=<span style=\"color: rgb(64, 112, 160)\">&#8220;2027-01-01&#8221;<\/span><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;<\/span><strong>pin<\/strong> digest=<span style=\"color: rgb(64, 112, 160)\">&#8220;SHA-256&#8221;<\/span><span style=\"color: rgb(0, 0, 0)\">&gt;PRIMARY_KEY_HASH_BASE64=&lt;\/<\/span><strong>pin<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;<\/span><strong>pin<\/strong> digest=<span style=\"color: rgb(64, 112, 160)\">&#8220;SHA-256&#8221;<\/span><span style=\"color: rgb(0, 0, 0)\">&gt;BACKUP_KEY_HASH_BASE64=&lt;\/<\/span><strong>pin<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&nbsp; &nbsp; &nbsp; &nbsp; &lt;\/<\/span><strong>pin-set<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&nbsp; &nbsp; &lt;\/<\/span><strong>domain-config<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<br \/>&lt;\/<\/span><strong>network-security-config<\/strong><span style=\"color: rgb(0, 0, 0)\">&gt;<\/span><\/p>\n<p>On iOS, keep App Transport Security on and justify every exception.<\/p>\n<p><strong>Test.<\/strong> Route the device through Burp Suite or mitmproxy with an untrusted certificate. A secure app refuses to connect.<\/p>\n<p><strong>Go deeper:<\/strong> M5 Insecure Communication: Finding and Fixing TLS Flaws; Network Security Config: Certificate Pinning on Android; App Transport Security (ATS): What Exceptions Really Cost You.<\/p>\n<p><strong>M6: Inadequate Privacy Controls<\/strong><\/p>\n<p><strong>What it is.<\/strong> Collecting, logging, or sharing personal data (PII) beyond what the app needs or the user agreed to. This is the category where security findings become GDPR, HIPAA, and DPDP problems.<\/p>\n<p><strong>Example.<\/strong> Email addresses and GPS coordinates land in crash logs and analytics events, then flow to a third-party dashboard.<\/p>\n<p><strong>Fix.<\/strong> Collect the minimum. Scrub PII from logs, crash reports, and analytics. Keep your Google Play Data Safety form and iOS privacy manifest accurate, and honor consent before any SDK initializes.<\/p>\n<p><strong>Test.<\/strong> Watch logcat and the iOS console during a full user journey. Inspect outbound analytics payloads in your proxy.<\/p>\n<p><strong>Go deeper:<\/strong> iOS Privacy Manifests: Requirements and Compliance Guide; GDPR and Mobile Apps: Data Collection Pitfalls; Google Play Data Safety Section: Getting It Right.<\/p>\n<p><strong>M7: Insufficient Binary Protections<\/strong><\/p>\n<p><strong>What it is.<\/strong> An app that is easy to reverse engineer, modify, and redistribute. This merges the old Code Tampering and Reverse Engineering categories.<\/p>\n<p><strong>Example.<\/strong> An attacker decompiles your app, removes the license check, re-signs it, and publishes a mod APK. Or they hook your root check with Frida and return false.<\/p>\n<p><strong>Fix.<\/strong> Obfuscate release builds with R8 or a commercial tool. Add runtime checks for root, jailbreak, emulators, debuggers, and hooking frameworks. Verify app integrity on the server with Play Integrity or App Attest, because any check that only runs on the device can be patched out. Runtime application self-protection (RASP) tools package these checks.<\/p>\n<p><strong>Test.<\/strong> Decompile the release build and check how readable it is. Repackage it and see whether the app or your backend notices. Try standard Frida bypass scripts.<\/p>\n<p><strong>Go deeper:<\/strong> What Is RASP for Mobile Apps?; How Attackers Reverse Engineer Android APKs; App Repackaging and Cloning Attacks: How to Stop Them.<\/p>\n<p><strong>M8: Security Misconfiguration<\/strong><\/p>\n<p><strong>What it is.<\/strong> Insecure defaults and leftover settings: debug flags, backups, exported components, and permissive platform settings.<\/p>\n<p><strong>Example.<\/strong> A release build has android:debuggable=&#8221;true&#8221;, or an exported Activity with no permission lets any installed app open an internal admin screen. On iOS, NSAllowsArbitraryLoads is left on from development.<\/p>\n<p><strong>Fix.<\/strong> Review the merged manifest for every release: debuggable, allowBackup, exported, permissions, and usesCleartextTraffic. Keep a separate, locked-down release configuration and fail the build if it drifts.<\/p>\n<p><strong>Test.<\/strong> Run aapt dump xmltree or open the manifest in jadx. Check Info.plist and entitlements. Use Drozer to call exported components directly.<\/p>\n<p><strong>Go deeper:<\/strong> M8 Security Misconfiguration in Android and iOS Apps; Android Manifest Security Review: What to Check; Exported Activities, Services, and Receivers: The Hidden Attack Surface.<\/p>\n<p><strong>M9: Insecure Data Storage<\/strong><\/p>\n<p><strong>What it is.<\/strong> Sensitive data saved on the device where other apps, backups, or anyone holding the phone can read it: plain SharedPreferences, NSUserDefaults, SQLite files, logs, caches, and screenshots.<\/p>\n<p><strong>Example.<\/strong> A refresh token sits in plain text in SharedPreferences. Anyone with a rooted device or an ADB backup reads it and takes over the account.<\/p>\n<p><strong>Fix.<\/strong> Store secrets in the Keychain on iOS and use Android Keystore-backed keys to encrypt data on Android. Choose the strictest accessibility level the feature allows.<\/p>\n<p><span style=\"color: rgb(0, 112, 32)\"><strong>let<\/strong><\/span> <span style=\"color: rgb(25, 23, 124)\">query<\/span><span style=\"color: rgb(102, 102, 102)\">:<\/span> <span style=\"color: rgb(102, 102, 102)\">[<\/span>String<span style=\"color: rgb(102, 102, 102)\">:<\/span> Any<span style=\"color: rgb(102, 102, 102)\">]<\/span> <span style=\"color: rgb(102, 102, 102)\">=<\/span> <span style=\"color: rgb(102, 102, 102)\">[<\/span><br \/>&nbsp; &nbsp; kSecClass <span style=\"color: rgb(0, 112, 32)\"><strong>as<\/strong><\/span> String<span style=\"color: rgb(102, 102, 102)\">:<\/span> kSecClassGenericPassword<span style=\"color: rgb(102, 102, 102)\">,<\/span><br \/>&nbsp; &nbsp; kSecAttrAccount <span style=\"color: rgb(0, 112, 32)\"><strong>as<\/strong><\/span> String<span style=\"color: rgb(102, 102, 102)\">:<\/span> <span style=\"color: rgb(64, 112, 160)\">&#8220;refresh_token&#8221;<\/span><span style=\"color: rgb(102, 102, 102)\">,<\/span><br \/>&nbsp; &nbsp; kSecValueData <span style=\"color: rgb(0, 112, 32)\"><strong>as<\/strong><\/span> String<span style=\"color: rgb(102, 102, 102)\">:<\/span> tokenData<span style=\"color: rgb(102, 102, 102)\">,<\/span><br \/>&nbsp; &nbsp; kSecAttrAccessible <span style=\"color: rgb(0, 112, 32)\"><strong>as<\/strong><\/span> String<span style=\"color: rgb(102, 102, 102)\">:<\/span> kSecAttrAccessibleWhenUnlockedThisDeviceOnly<br \/><span style=\"color: rgb(102, 102, 102)\">]<\/span><br \/><span style=\"color: rgb(0, 112, 32)\"><strong>let<\/strong><\/span> <span style=\"color: rgb(25, 23, 124)\">status<\/span> <span style=\"color: rgb(102, 102, 102)\">=<\/span> SecItemAdd<span style=\"color: rgb(102, 102, 102)\">(<\/span>query <span style=\"color: rgb(0, 112, 32)\"><strong>as<\/strong><\/span> CFDictionary<span style=\"color: rgb(102, 102, 102)\">,<\/span> <span style=\"color: rgb(0, 112, 32)\"><strong>nil<\/strong><\/span><span style=\"color: rgb(102, 102, 102)\">)<\/span><\/p>\n<p><strong>Test.<\/strong> On a rooted or jailbroken test device, walk the app\u2019s sandbox (\/data\/data\/&lt;package&gt; on Android, the app container on iOS) after a full session and grep for tokens, emails, and card data.<\/p>\n<p><strong>Go deeper:<\/strong> M9 Insecure Data Storage: Where Mobile Apps Leak Data; Android Keystore: How to Store Secrets Correctly; iOS Keychain Security: Correct Usage and Common Pitfalls.<\/p>\n<p><strong>M10: Insufficient Cryptography<\/strong><\/p>\n<p><strong>What it is.<\/strong> Weak algorithms, bad modes, hardcoded keys, static IVs, or home-made crypto.<\/p>\n<p><strong>Example.<\/strong> The app encrypts local data with AES in ECB mode using a key stored as a string constant, so the encryption adds nothing.<\/p>\n<p><strong>Fix.<\/strong> Use platform crypto APIs with modern defaults: AES-GCM for data, keys generated and held in Keystore or Keychain (or the Secure Enclave), and a fresh random nonce per encryption. Never use MD5 or SHA-1 for anything security-relevant.<\/p>\n<p><strong>Test.<\/strong> Search decompiled code for AES\/ECB, DES, MD5, SHA1, and hardcoded byte arrays. Hook crypto calls with Frida to see keys and IVs at runtime.<\/p>\n<p><strong>Go deeper:<\/strong> Securing Swift Apps: Data Protection Classes Explained; Android Keystore: How to Store Secrets Correctly.<\/p>\n<p><strong>How the Top 10 maps to MASVS and MASTG<\/strong><\/p>\n<p>The Top 10 tells you what to worry about; the OWASP MAS project tells you exactly what to build and test. Use the Top 10 to prioritize and explain risk, and use MASVS as the requirement you verify against.<\/p>\n<ul>\n<li>\n<p><strong>MASVS<\/strong> (Mobile Application Security Verification Standard) is the set of security controls, grouped into eight areas: STORAGE, CRYPTO, AUTH, NETWORK, PLATFORM, CODE, RESILIENCE, and PRIVACY (<a target=\"_blank\" href=\"https:\/\/mas.owasp.org\/MASVS\/\" rel=\"noopener noreferrer\"><span style=\"color: rgb(79, 129, 189)\">OWASP MASVS<\/span><\/a>).<\/p>\n<\/li>\n<li>\n<p><strong>MASTG<\/strong> (Mobile Application Security Testing Guide) holds the test procedures for Android and iOS that prove each control.<\/p>\n<\/li>\n<li>\n<p><strong>MASWE<\/strong> (Mobile Application Security Weakness Enumeration) lists the specific weaknesses that sit between the two.<\/p>\n<\/li>\n<\/ul>\n<p>MASVS v2.0 removed the old L1, L2, and R levels from the standard. They now live in MASTG as testing profiles, so a banking app can be tested at L2 plus resilience while a simple utility app stops at L1 (<a target=\"_blank\" href=\"https:\/\/approov.io\/blog\/a-practical-guide-to-owasp-masvs-v2\" rel=\"noopener noreferrer\"><span style=\"color: rgb(79, 129, 189)\">Approov<\/span><\/a>).<\/p>\n<p style=\"text-align: center\">OWASP Mobile Top 10 (2024)<\/p>\n<p style=\"text-align: center\">Main MASVS control group<\/p>\n<p>M1 Improper Credential Usage<\/p>\n<p>MASVS-AUTH, MASVS-CRYPTO<\/p>\n<p>M2 Inadequate Supply Chain Security<\/p>\n<p>MASVS-CODE<\/p>\n<p>M3 Insecure Authentication\/Authorization<\/p>\n<p>MASVS-AUTH<\/p>\n<p>M4 Insufficient Input\/Output Validation<\/p>\n<p>MASVS-CODE, MASVS-PLATFORM<\/p>\n<p>M5 Insecure Communication<\/p>\n<p>MASVS-NETWORK<\/p>\n<p>M6 Inadequate Privacy Controls<\/p>\n<p>MASVS-PRIVACY<\/p>\n<p>M7 Insufficient Binary Protections<\/p>\n<p>MASVS-RESILIENCE<\/p>\n<p>M8 Security Misconfiguration<\/p>\n<p>MASVS-PLATFORM, MASVS-CODE<\/p>\n<p>M9 Insecure Data Storage<\/p>\n<p>MASVS-STORAGE<\/p>\n<p>M10 Insufficient Cryptography<\/p>\n<p>MASVS-CRYPTO<\/p>\n<p><strong>Go deeper:<\/strong> OWASP MASVS Explained: Levels, Controls, and How to Comply; MASVS vs MASTG: What\u2019s the Difference and Which Do You Need?<\/p>\n<p><strong>How to test your app against the Top 10<\/strong><\/p>\n<p>No single tool covers all ten risks. Automated scanning catches most of M1, M2, M8, M9, and M10; M3, M4, and M7 need a person, or an AI agent, actively attacking the running app.<\/p>\n<ol>\n<li>\n<p><strong>Static analysis (SAST) on every pull request.<\/strong> Scan source or the built APK\/IPA for hardcoded secrets, weak crypto, insecure storage calls, and manifest flags. Open-source options include MobSF; commercial and AI-assisted tools cut down false positives.<\/p>\n<\/li>\n<li>\n<p><strong>Dependency and SBOM checks on every build.<\/strong> Run SCA across Gradle, CocoaPods, SwiftPM, npm, and pub, and store an SBOM with each release. This is your main control for M2.<\/p>\n<\/li>\n<li>\n<p><strong>Dynamic testing on a real or emulated device.<\/strong> Proxy traffic through Burp Suite or mitmproxy, inspect storage after a full session, and exercise deep links and exported components. This covers M4, M5, M6, and M9.<\/p>\n<\/li>\n<li>\n<p><strong>Runtime and resilience testing.<\/strong> Use Frida and objection to bypass root checks, pinning, and client-side authorization. If a bypass works, confirm the server still blocks the action. This covers M3 and M7.<\/p>\n<\/li>\n<li>\n<p><strong>Manual or AI-assisted pentest before major releases.<\/strong> A tester chains findings into real attack paths, which scanners cannot do. Schedule one at least yearly and before launches that touch payments, health data, or auth.<\/p>\n<\/li>\n<\/ol>\n<p>Put steps 1 and 2 in CI so they block merges on high-severity findings. Run step 3 nightly or per release candidate.<\/p>\n<p><strong>Go deeper:<\/strong> Mobile Application Security Testing (MAST): The Complete Guide; SAST vs DAST vs IAST for Mobile Apps; Integrating Mobile Security Testing into CI\/CD Pipelines; Frida for Mobile Security Testing: A Practical Intro.<\/p>\n<p><strong>Where to start: a 10-point checklist<\/strong><\/p>\n<p>If you can only fix a few things this sprint, start at the top. These are ordered by how often they lead to real account takeover or data loss.<\/p>\n<ul>\n<li>\n<p>\u2610 No live secrets in the app binary; every shipped key rotated (M1)<\/p>\n<\/li>\n<li>\n<p>\u2610 Every API endpoint checks authorization on the server (M3)<\/p>\n<\/li>\n<li>\n<p>\u2610 TLS validation untouched, cleartext blocked, pinning for high-risk APIs (M5)<\/p>\n<\/li>\n<li>\n<p>\u2610 Tokens and PII only in Keystore or Keychain (M9)<\/p>\n<\/li>\n<li>\n<p>\u2610 debuggable off, backups restricted, no unprotected exported components (M8)<\/p>\n<\/li>\n<li>\n<p>\u2610 Deep links, intents, and WebViews validate their input (M4)<\/p>\n<\/li>\n<li>\n<p>\u2610 Dependencies pinned, SBOM generated per release (M2)<\/p>\n<\/li>\n<li>\n<p>\u2610 No PII in logs, crash reports, or analytics (M6)<\/p>\n<\/li>\n<li>\n<p>\u2610 Release builds obfuscated, integrity verified server-side (M7)<\/p>\n<\/li>\n<li>\n<p>\u2610 AES-GCM with platform-held keys; no MD5, SHA-1, or ECB (M10)<\/p>\n<\/li>\n<\/ul>\n<p><strong>Go deeper:<\/strong> Mobile App Security Checklist for 2026 (Free Template).<\/p>\n<p><strong>FAQ<\/strong><\/p>\n<p><strong>What is the OWASP Mobile Top 10?<\/strong> It is a ranked list of the ten most critical security risks in mobile applications, published by the Open Worldwide Application Security Project (OWASP). The current edition is 2024.<\/p>\n<p><strong>What is new in the OWASP Mobile Top 10 2024?<\/strong> Four categories are new: Improper Credential Usage, Inadequate Supply Chain Security, Insufficient Input\/Output Validation, and Inadequate Privacy Controls. Authentication and authorization merged, and code tampering and reverse engineering merged into Insufficient Binary Protections.<\/p>\n<p><strong>Is the OWASP Mobile Top 10 a compliance standard?<\/strong> No.&nbsp;It is an awareness document. For verifiable requirements, use OWASP MASVS and test with MASTG. Auditors and enterprise buyers often ask for MASVS alignment.<\/p>\n<p><strong>How often should I test my app against the Top 10?<\/strong> Run automated checks on every build, dynamic tests per release, and a full pentest at least once a year or before major releases.<\/p>\n<p><strong>Does the Top 10 apply to React Native and Flutter apps?<\/strong> Yes. Cross-platform apps compile to the same Android and iOS packages and face the same risks, plus extra ones from JavaScript bundles and plugin dependencies.<\/p>\n<p><strong>Next step: test your app<\/strong><\/p>\n<p>The Top 10 is only useful if you check your own build against it. Start with the checklist above, then automate what you can so every release is tested the same way.<\/p>\n<p>Nandee is an AI-assisted mobile application security platform for testing Android and iOS apps and protecting them at runtime.<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The OWASP Mobile Top 10 (2024) explained for developers: what each of the ten risks looks like in a real Android or iOS app, how to fix it, and how to test for it.<\/p>\n","protected":false},"author":1,"featured_media":100009,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-100010","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/posts\/100010","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/comments?post=100010"}],"version-history":[{"count":0,"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/posts\/100010\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/media\/100009"}],"wp:attachment":[{"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/media?parent=100010"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/categories?post=100010"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.nandee.ai\/blog\/wp-json\/wp\/v2\/tags?post=100010"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}