Skip to main content
News/Kasada

Kasada Mobile SDK v2: The User-Agent Is Now an MD5 Hash

Kasada's v2 mobile SDK no longer sends a readable user-agent. It sends a deterministic MD5 hash of the OS version, app bundle ID and app version. Here is what changed, what goes into the hash, and how we confirmed it against live traffic.

Hyper Solutions5 min read
On this page
Kasada
TLS fingerprint
Header order
Client hints

Kasada Mobile SDK v2 changes how the user-agent is sent, and the change is easy to miss.

In the older mobile SDK, the user-agent was an ordinary string that the app handed to the SDK at startup. In Mobile SDK v2, the SDK builds the value itself. It still travels in the User-Agent header, but it is no longer a user-agent in any readable sense. It is 32 hexadecimal characters:

7016adc4ba8d433533954ebad33be424

That is an MD5 hash. Because it sits in a header everyone expects to contain a browser or app string, it is easy to look past. At Hyper Solutions we pulled a current build apart to see what goes into the hash, and then checked the result against live traffic from a real app.

Key Takeaways

  • In Kasada Mobile SDK v2, the User-Agent header carries an MD5 hash, not a readable user-agent string.
  • The hash input is a single line: <OS version> / <app bundle ID> <app version>.
  • The value is computed inside the SDK. The app no longer passes a user-agent string in.
  • There is no per-request randomness. The same OS version, bundle ID and app version always produce the same value.
  • We confirmed the formula against live captures from a real iOS app, across two of its releases.
  • A browser-style user-agent will never match the native Kasada v2 format. The value has to be derived, and it has to stay consistent with the rest of the device fingerprint.

What changed between the old mobile SDK and v2

The change at a glance:

  • Old mobile SDK: a fixed user-agent string, passed in by the app at startup and sent as is.
  • Mobile SDK v2: an MD5 hash of the OS version, bundle ID and app version, computed inside the SDK.

The header name stayed the same, but its content changed completely. What used to be a readable description of the client is now a single hash of the app and the OS.

That is what makes the change interesting. The user-agent is no longer something the app supplies. The SDK derives it from the environment it runs in. As a result, the value is tied to a specific app build on a specific OS version rather than to whatever the integrating app chooses to report.

What goes into the hash

The input format

The input is one string, in this exact format:

<OS version> / <app bundle ID> <app version>

The separators matter. There is a space, a forward slash and another space between the OS version and the bundle ID, and a single space before the app version. The SDK runs that string through MD5, and the 32-character hex digest is what ends up in the header.

An iOS example

On an iPhone, the input looks like this:

18.0.1 / com.example.app 9.15.0

Hashed with MD5, that becomes:

7016adc4ba8d433533954ebad33be424

The three input fields

The three fields are:

  • OS version: the version of the operating system the app runs on, such as 18.0.1 on iOS.
  • App bundle ID: the identifier of the app, such as com.example.app.
  • App version: the version of the app build, such as 9.15.0.

Why the value is deterministic

The hash contains only device and app identity: no timestamp, no nonce, nothing that changes per request. For a given app build on a given OS version, the value never changes. Two requests from the same app version on the same OS version carry the same User-Agent value.

That has two practical consequences:

  1. It looks like an identifier. Because the value is constant and sits in the User-Agent header, it is easy to mistake for a static token or an opaque ID if you don't know where it comes from.
  2. It changes when the app or the OS changes. An app update or an OS update produces a different hash. A value captured once and reused later will drift out of step with the app version it is supposed to represent.

What this means for mobile fingerprinting and automation

For anyone working on mobile automation or mobile reverse engineering, the conclusion is simple: a browser-style user-agent will never match a native Kasada v2 flow.

The value is derived from OS and app information, so it has to line up with the OS version and app version described by the rest of the device fingerprint. A user-agent that contradicts those other signals is inconsistent by construction.

It also illustrates a broader pattern. Mobile SDKs increasingly derive values that used to be configurable. To understand what a header actually means, you have to look at the code that produces it, not just at the traffic.

How we confirmed it against live traffic

We pulled a current build of a Kasada-protected app apart, located where the v2 SDK produces the user-agent, and identified the input it hashes: OS version, bundle ID and app version, in the format shown above.

Then we checked it the only way that counts: against live traffic. Captures from a real iOS app on two of its releases both matched the formula on the first try once we plugged in the corresponding OS version, bundle ID and app version.

SDK support

The Hyper Solutions Go, Python and JavaScript SDKs ship a one-call helper that returns this value, so you don't have to reimplement the hash yourself. The helper is a pure, local function: it makes no API call.

Go (hyper-sdk-go v3.1.0):

import "github.com/Hyper-Solutions/hyper-sdk-go/v3/kasada"

userAgent := kasada.GenerateUserAgent("18.0.1", "com.example.app", "9.15.0")
// 7016adc4ba8d433533954ebad33be424

Python (hyper_sdk 3.1.0):

from hyper_sdk import generate_kasada_user_agent

user_agent = generate_kasada_user_agent("18.0.1", "com.example.app", "9.15.0")
# 7016adc4ba8d433533954ebad33be424

JavaScript / TypeScript (hyper-sdk-js 4.1.0):

import { generateKasadaUserAgent } from 'hyper-sdk-js';

const userAgent = generateKasadaUserAgent({
    osVersion: '18.0.1',
    bundleId: 'com.example.app',
    appVersion: '9.15.0',
});
// 7016adc4ba8d433533954ebad33be424

For an overview of how Hyper Solutions handles Kasada today, see the Kasada product page.

Questions about your mobile flow?

If your mobile Kasada flow behaves differently from what is described here, come and talk to us on Discord. We are happy to look at a capture with you.

Working examples for every language and TLS client are in the examples repo, and the step-by-step Kasada integration guide is in our documentation.

Try it yourself

Bypass Kasada with a single API call

Skip the browser. Generate the sensor data, tokens and cookies you just read about over plain HTTP, with a free one-week trial.