Android

    Android Deep Link Security Vulnerabilities Explained

    An Android custom-scheme deep link carrying an auth token being intercepted by a second app that registered the same scheme.

    Android deep link security vulnerabilities come down to two facts. First, a standard custom-scheme deep link is not verified, so any app on the device can register the same scheme and intercept the link, which lets a malicious app capture whatever the link carries, such as an auth token, and hijack the account. Second, the data inside a deep link is attacker-controlled input, so if your app trusts it without validation it can drive open redirects, WebView injection, or file access. The two mitigations are Android App Links, which bind an http link to a domain you own so no other app can claim it, and strict validation of every parameter a deep link delivers.

    Short answer

    The vulnerability is that unverified deep links are not exclusively yours and their contents are not trustworthy. Per Android's documentation, a standard deep link uses the intents system and is subject to the system disambiguation dialog because a custom scheme can be claimed by multiple apps, so a hostile app can register your scheme and receive the intent. Android App Links fix that by verifying http links against a Digital Asset Links file on your domain, so they open your app directly and cannot be claimed by another app. Beyond hijacking, treat every deep link parameter as untrusted input and validate it, since per OWASP inter-app communication and data received from other apps must be handled as untrusted.

    A deep link is a URI that opens a specific screen inside your app instead of just launching it, and Android routes it through the intents system. When your app declares an intent filter for a scheme or a host, the system knows that links matching it can be handled by your app, and tapping such a link either opens your app directly or offers it as a choice.

    The mechanism that makes this convenient is the same one that creates the risk. Because routing is based on intent filters that any app can declare, the platform has to decide which app receives a link when more than one claims it. For an unverified custom scheme, that decision can involve a chooser dialog or, worse from a security view, silent delivery to whichever app is installed. So the routing that delivers your users to the right screen is also the routing an attacker can insert themselves into if the link is not bound to you.

    The core distinction the query asks about is verification, and it separates three things Android treats differently. A standard deep link, in Android's words, takes advantage of the intents system to route links to your app, is available on all Android versions, and is subject to the system disambiguation dialog because it is not verified. A web link is simply a deep link that uses the http or https scheme. An Android App Link is an enhanced deep link that verifies the link against your own website by establishing a trusted association between your app and your domain.

    That trusted association is a Digital Asset Links file, assetlinks.json, hosted on your domain, and it is what changes the security picture. Per Android, once verified, App Links open the corresponding content in your app without requiring the user to select your app from a disambiguation dialog, and, critically, another app cannot claim a verified http link for a domain it does not control. App Links are supported on Android 6 and later on devices with Google services. So the practical rule is that deep links to your own website should use App Links, because verification is what makes the link exclusively yours.

    An unverified custom-scheme deep link is hijackable because scheme ownership is not enforced, so more than one app can declare the same scheme and compete for the intent. If your app uses a custom scheme like myapp colon slash slash and a malicious app on the same device declares an intent filter for that scheme, the system may route the link to the attacker's app, either through a chooser the user misreads or silently. Whatever the link carries then lands in the attacker's hands.

    This is exactly how deep links turn into account hijacking. A common pattern passes an OAuth authorization code or a session token back to the app through a custom-scheme redirect, and if a hostile app intercepts that redirect, it receives the code and can complete the sign-in as the victim. The spoofing works because the platform trusted the scheme claim rather than a verified identity. Binding the redirect to a verified App Link on a domain you control removes the ambiguity, because the attacker cannot register your domain, which is why sensitive redirects should never ride on an unverified custom scheme.

    Even a link that reaches the right app is dangerous if the app trusts its contents, because a deep link is a message from outside your trust boundary. Anyone can craft a URI with any path and any parameters and send it to your app, so the values inside are attacker-controlled. If your handler takes a parameter and loads it into a WebView, uses it as a file path, builds a database query from it, or treats it as an already-authenticated instruction, an attacker can supply a value that causes open redirection, script injection, path traversal, or unauthorized actions.

    So the second class of deep link vulnerability has nothing to do with who receives the link and everything to do with what the app does with it. A link that opens a reset-password screen must still verify the token server-side rather than trusting a flag in the URI. A link that renders a URL in a WebView must confirm the URL is one you allow. The safe posture is to treat every deep link the way you would treat a form submitted by an anonymous stranger, because that is effectively what it is.

    The differences that matter for security are verification and exclusivity, which the table below lays out.

    Link typeSchemeVerified?assetlinks.jsonCan another app claim it?
    Deep linkCustom or httpNoNot usedYes, via the same intent filter
    Web linkhttp or httpsNoNot usedYes, shown in the chooser
    Android App Linkhttp or httpsYesRequired on your domainNo, bound to your domain

    Read the last column as the security summary: only a verified App Link cannot be claimed by another app, which is why it is the right choice for any link that carries something sensitive.

    Safe handling has two halves that map to the two risks, and both are needed. To stop hijacking, use App Links for links to your website so verification binds them to your domain, and never pass secrets such as tokens or authorization codes through an unverified custom scheme. To stop input abuse, validate every part of the incoming link before acting on it: check the path against an allowlist of screens, validate and sanitize each parameter, and confirm any URL you would open or render is one you permit.

    The rule underneath both halves is that the deep link decides where to go, not whether the user is allowed to go there. Authentication and authorization still happen against your backend, using the session, not against a value in the URI. So a reset link carries a token you verify server-side, a screen route is matched against known routes rather than reflected blindly, and anything the link cannot prove remains untrusted. Handled this way, a deep link becomes a convenient pointer rather than a trusted command.

    On your own app and a device you control, you can confirm both risks without special tooling. Inspect your manifest for exported activities with intent filters on custom schemes, since those are the entry points, and use the Android debug bridge to fire a deep link at your app with crafted parameters, for example an unexpected path or a URL parameter, to see whether the handler validates it or acts on it blindly. A static analysis tool such as MobSF can list your intent filters and flag exported components for you.

    To probe hijacking specifically, keep it to your own test devices and confirm whether a sensitive value travels through a custom scheme, because that is the token an interceptor would capture. The point of the exercise is to see where a link enters your app and what your code trusts about it, which is the boundary any responsible assessment stays within. If a crafted link changes behavior it should not, or a secret rides an unverified scheme, you have found the fix: verify the link and validate the input.

    Remediation checklist

    Working through these steps closes the common deep link gaps. The checklist below covers them.

    StepActionDone?
    Verify your linksUse App Links with assetlinks.json for your domain[ ]
    Remove secrets from URIsStop passing tokens or codes over custom schemes[ ]
    Allowlist routesMatch incoming paths against known screens[ ]
    Validate parametersSanitize and check every value before use[ ]
    Enforce auth server-sideVerify tokens on the backend, not from the URI[ ]
    Audit intent filtersConfirm exported components expect untrusted input[ ]

    The step teams skip most is the last one, since an exported activity with a deep link filter is the actual attack surface and is easy to overlook.

    Where a scan fits

    After you verify your links and validate their input, checking the whole app for the same pattern is worthwhile, because deep link handlers are rarely the only place untrusted input enters.

    A scanner like PTKD.com analyzes your build and flags issues such as exported components that accept deep links without validation, secrets that could travel through them, and over-broad permissions by severity, mapped to OWASP MASVS. To be clear about the boundary: PTKD reports these findings for you to fix; it does not verify your App Links or rewrite your intent handlers. It points at the deep link entry points so you can bind and validate each one.

    What to take away

    • Standard custom-scheme deep links are not verified, so another app can claim the scheme and intercept the link, which is how deep links become account hijacking.
    • Android App Links fix that by verifying http links against a Digital Asset Links file on your domain, so no other app can claim them and they open your app directly.
    • Deep link parameters are attacker-controlled input, so validate every path and value and never let a URI stand in for authentication.
    • Never pass tokens or authorization codes through an unverified custom scheme, and enforce auth server-side against the session.
    • Test your own deep links with the debug bridge and a manifest review, and a tool like PTKD.com helps flag unvalidated deep link handlers app-wide.
    • #android
    • #deep links
    • #app links
    • #intent security
    • #owasp masvs

    Frequently asked questions

    What is the difference between an Android deep link and an App Link?
    A standard deep link uses the intents system, is available on all Android versions, and is not verified, so it is subject to the system disambiguation dialog and its custom scheme can be claimed by more than one app. An Android App Link is a verified deep link that establishes a trusted association between your app and your website through a Digital Asset Links file (assetlinks.json) hosted on your domain, so it opens your app directly without a chooser and cannot be claimed by another app. Use App Links for any link to your website.
    Why can an unverified deep link be hijacked?
    Because scheme ownership is not enforced. If your app uses a custom scheme and a malicious app declares an intent filter for the same scheme, the system may route the link to the attacker's app, either through a chooser the user misreads or silently. Whatever the link carries then reaches the attacker. This is how account hijacking happens: an OAuth code or session token passed back through a custom-scheme redirect can be intercepted. Binding the redirect to a verified App Link on a domain you control removes the ambiguity.
    How do I validate deep link input safely?
    Treat every deep link as attacker-controlled input, because anyone can craft a URI and send it to your app. Match the incoming path against an allowlist of known screens, validate and sanitize each parameter, and confirm any URL you would open or render is one you permit. Keep authentication and authorization on your backend against the session, not on a value in the URI, so a reset link carries a token you verify server-side rather than a flag the app trusts. Never let a URI stand in for auth.
    Should I pass tokens through a deep link?
    Not through an unverified custom scheme. Because another app can register the same scheme and intercept the redirect, any authorization code or session token that travels over a custom scheme can be captured and used to hijack the account. If you must return a value to the app, use a verified App Link bound to a domain you own so no other app can claim it, and still verify the token server-side. The safest design keeps secrets out of the URI and completes the exchange against your backend.
    How can I test my own app's deep link security?
    On your own app and a device you control, review your manifest for exported activities with intent filters on custom schemes, since those are the entry points, and use the Android debug bridge to fire deep links with crafted paths and parameters to see whether the handler validates them or acts blindly. A static analysis tool such as MobSF can list your intent filters and flag exported components. Keep testing to apps and devices you own or are authorized to assess, and fix any link that changes behavior it should not.
    Do deep link risks map to OWASP MASVS?
    Yes. Deep link handling falls under inter-app communication in the OWASP Mobile Application Security Verification Standard, which requires that data received from other apps, including through deep links and intents, be treated as untrusted and validated, and that sensitive functionality not be exposed through unprotected components. The OWASP testing guide describes checking how an app handles deep links and exported components. Verifying your links with App Links and validating their input is how you meet those platform-interaction requirements.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free