WarpLink
Trust and reliability

Abuse Protection and Threat Model

What an extracted SDK key can do, how fabricated installs, replay, and referral manipulation work, which controls exist, and how to verify rewards server-side.

Install attribution is directional evidence, not payout-grade proof. An SDK key ships inside your app, so anyone can extract it, and the attribution endpoint cannot prove that a real device installed your app. If a result pays money or grants a reward, confirm it on your server first, for example by requiring a real sign-up or purchase before paying a referral. This page states the threats, the controls that exist in our code today, and the gaps they leave.

What the attacker holds

Assume the attacker has read your app binary and has your SDK key. The key is publishable by design.

An SDK key canAn SDK key cannot
Call GET /v1/sdk/validate, GET /v1/links/resolve/{slug}, and POST /v1/attribution/matchCall any other REST endpoint, or the MCP server
Read a link's routing, including its ID, through resolveList your links, apps, or domains, or read analytics
Submit attribution requests for your organizationCreate, edit, or delete anything

The key holds two scopes, links:read and attribution:write, and the server clamps an SDK key to those two scopes even if a stored row says more. A key that is tied to one app acts only as that app. A request that names another app gets 403 KEY_APP_MISMATCH, and the key never reads or claims another app's links or installs. See Authentication.

The key is worth little for theft of data, and worth more for pollution of data. The threats below are about pollution.

Threats and controls

Fabricated installs

An attacker with the key can send attribution requests from a script. Two paths matter.

  • Referrer path (Android). The request carries a link ID as the install referrer. The server accepts a referrer only if it names a link in the key's own organization, and in the key's own app for a tied key. It cannot prove the install happened. A request with no device ID and no tap identifier records one install each time, because the server treats a request it cannot tie to a device or a tap as new rather than risk dropping a real install. A script can also send a fresh random device ID with every request.
  • Fingerprint path. A request is matched against a click that really reached our redirect service from the same network. The attacker needs to have clicked the link first, and each stored click can be claimed by one install only. A uniqueness rule on the click blocks a second claim.

Controls that exist:

  • Rate limit. The three SDK endpoints allow 60 requests per minute for each key and client IP address pair. IPv6 addresses count by their /64 prefix, so rotating the host part of an address does not create new buckets. If the rate-limit store cannot be reached, requests are let through rather than refused.
  • App-tied keys. A key tied to one app limits the damage of an extracted key to that app.
  • Revocation. You can revoke an SDK key in the dashboard and ship a new one. A blocked organization's SDK keys are refused outright.
  • Attempt records. Each attribution request that passes validation is recorded as an attempt with its outcome and, for a miss, a failure reason. The match-health figures in your dashboard are built from these records, so a burst of misses is visible.

What does not exist: we do not verify device attestation from the operating system, and we do not require a signed proof that the app is a genuine build. A script that holds the key looks like a device to us.

Click inflation

The resolve endpoint records a click when an SDK key calls it with an SDK user agent, because that is how an app open is counted. A script can do the same.

  • Uniqueness. A click is unique only if no click on the same link from the same hashed IP address falls within 24 hours either side of it. Billing counts unique clicks only.
  • Rate limit. The same 60 requests per minute per key and client IP applies.
  • Bot screening. The resolve endpoint does not record a click for a request that looks like a crawler.

A script that rotates through many IP addresses can still add unique clicks. Treat click counts as traffic measurement, not as proof of distinct people.

Replay

An attribution request has no signature or one-time token, so an attacker can send a captured request again.

  • A device that already has an install. A repeat request that carries the same device ID returns the install that device already has. It does not record a second one. A reinstall is the exception: it is counted as a new install.
  • Retries of one tap. The SDK sends a tap identifier with each attempt. A repeat of the same tap returns the same install.
  • Fingerprint claims. A stored click is consumed by the first install that claims it. A replay of a claim finds nothing left to claim.
  • Match window. A fingerprint can only match a click inside the link's match window, 6 hours by default and 24 at most. Replay long after the tap finds no click.

Replay protection has one deliberate exception. A request that sets is_reinstall to true is never answered with an earlier install, because a reinstall must be counted as a new install. On the referrer path that means a request can create another install even when it repeats the same tap identifier.

The gap is the referrer path described above: with no device ID or tap identifier, a replay records a new install.

Referral and reward manipulation

Three weaknesses sit under any referral program built on attribution.

  • Link parameters are user input. A link can forward query parameters from the tapped URL. Anyone can edit a query string before opening it, so a referral code that arrives this way can be forged. Treat it as untrusted input.
  • Shared networks. Several people behind one public IP address, such as an office or a mobile carrier gateway, can cause an install to match another person's tap. The confidence score drops when the address looks shared, and it is never zero. See Attribution accuracy.
  • Self-referral. Nothing in attribution knows that the person who tapped and the person who installed are the same account.

What the results mean

A match carries a type and a confidence. Only an Android install matched through the Play Install Referrer has type deterministic, and even that proves the install came through the store listing a link opened, not that the person is who they claim to be. Every other match is probabilistic, a best guess from a hash of the network address, language, and time zone. The confidence score is not a probability.

Use a match to route the user to the right screen. Do not use it to sign anyone in, to unlock paid features, or to pay anyone.

Pay rewards server-side

  1. Require a real action. Pay a referral only after the referred person signs up, verifies an email or phone number, or completes a purchase that you have confirmed in your own backend.
  2. Bind the reward to your account system. Store the referral link or code against the referrer's account on your server. Do not trust a code the client sends back.
  3. Limit what one referrer can earn. Cap rewards per referrer and per period, and hold them for a review period.
  4. Compare accounts. Reject a reward when the referrer and the referred person share a device, a payment method, or an email address.
  5. Watch for bursts. A run of installs from one link with no sign-ups after them is a signal. Your backend shows it.
  6. Use unguessable codes. If a reward depends on a code, generate it on your server and do not put a guessable counter in a link.

We check link destinations before they can be saved. On the Free plan, each web destination is checked against threat lists for malware, social engineering, and unwanted software, and a flagged destination is refused. If the lookup service is unavailable, the save goes ahead and we log the failure. We can also block a Free organization. A blocked organization's links stop redirecting and its keys are refused.

Reporting abuse

Report abuse of the service or of a link to legal@warplink.app. Report a vulnerability in our systems to security@warplink.app, as described on the security page.

On this page