This article outlines how we protect the profiling blob from interception, modification, and replay. It covers the signals that surface when these attacks are attempted.
The Profiling Blob
Our JavaScript and Mobile SDK profiling collects device forensics, connection data, and behavioral biometrics on the client. When the user triggers an action, this data is packaged into a base64-encoded protobuf payload called the profiling blob (~2–8 KB depending on permissions and behavioral contexts collected). It contains:
- Device fingerprint data (screen, timezone, GPU, fonts, canvas, audio context)
- Behavioral biometrics (keystroke dynamics, mouse/touch, sensor data)
- Secure ID cryptographic material (keys, nonce, signature)
- Profiling metadata (handle, version, timestamps)
The blob contains only profiled attributes from the device and connection. There is no user-submitted input or credentials
Within the blob, some fields like user agent and timezone are clear text, but most values are obfuscated, and the internal schema is not published.
Transmission
All Darwinium communication operates exclusively over HTTPS. The dwn-profiling header is always encrypted in transit via TLS.
The way it reaches the Darwinium engine depends on deployment mode:
Edge worker deployment
The client attaches the blob as the dwn-profiling header. Our edge worker extracts it, strips the header, performs risk assessment, then forwards the request to your origin with dwn-profiling removed (and optionally risk headers added).
.png?sv=2026-02-06&spr=https&st=2026-07-07T04%3A10%3A33Z&se=2026-07-07T04%3A24%3A33Z&sr=c&sp=r&sig=3Z6bZ9qAiD1Gf1ACbCQH%2F%2FVbgUK9SLJcWnlEg99FV%2Fw%3D)
Tags/API deployment
The Javascript Tags or Mobile SDK collects the blob via collect() and returns it to your frontend. Your front end is responsible to for passing to backend, which includes it as the dwn-profiling header when calling the Darwinium API directly.
.png?sv=2026-02-06&spr=https&st=2026-07-07T04%3A10%3A33Z&se=2026-07-07T04%3A24%3A33Z&sr=c&sp=r&sig=3Z6bZ9qAiD1Gf1ACbCQH%2F%2FVbgUK9SLJcWnlEg99FV%2Fw%3D)
CORS: If enforced,
dwn-profilingmust be in the allowed headers list.
Profiling Presence & Validity
The profiling.source attribute indicates whether the blob was produced by Javascript, iOS SDK or Android SDK, or is NULL when profiling is missing or invalid.
Signals inside profiling.device.signals confirm whether the dwn-profiling header contained a valid, decodable blob.
Any structural deviation from what the profiling code is expected to produce, raises INVALID_PROFILING_BLOB. When this happens, the event processes without profiling enrichment. The queries below can be used as rules to decision accordingly on those scenarios.
| Check | Query | |
|---|---|---|
| Profiling valid | profiling.source = 'browser' / 'android_sdk' / 'ios_sdk' |
|
| Profiling not present | profiling.source = NULL |
|
| Blob header present | has(profiling.device.signals, 'RECEIVED_PROFILING_BLOB') |
|
| Header present but empty | has(profiling.device.signals, 'EMPTY_PROFILING_BLOB') |
|
| Profiling error | has(profiling.device.signals, 'ERROR_ON_PROFILING') |
|
| Blob tampered or corrupted | has(profiling.device.signals, 'INVALID_PROFILING_BLOB') |
|
| No profiling handle in blob | has(profiling.device.signals, 'NO_PROFILING_HANDLE') |
|
| Handle not found in backend; expires after 24hours first seen, attributes like ipinfo and cookies will not resolve | has(profiling.device.signals, 'HANDLE_NOT_FOUND') |
Secure ID
Secure ID is a cryptographic device identity mechanism embedded in the profiling blob. It uses an ECDSA key pair generated and stored on the device to provide proof-of-device that is resistant to cookie copying.
- The private key is set as non-extractable and never leaves the device, so even if an attacker steals all session cookies, they cannot forge the signature.
- The single-use nonce also serves as the primary replay detection mechanism (see Replay Detection below).
Key Exchange Flow
- On first visit, the JS or Mobile SDK generates an ECDSA key pair on the device
- Keys are stored persistently: IndexedDB (web), KeyStore (Android), KeyChain (iOS)
- Our server provides a unique, single-use nonce
- The client signs the nonce with the private key
- The signed nonce, public key, and metadata are included in the blob
- Server-side: we verify the signature, derive the Secure ID from public key + creation time
- The nonce is permanently and atomically marked and cannot be reused
.png?sv=2026-02-06&spr=https&st=2026-07-07T04%3A10%3A33Z&se=2026-07-07T04%3A24%3A33Z&sr=c&sp=r&sig=3Z6bZ9qAiD1Gf1ACbCQH%2F%2FVbgUK9SLJcWnlEg99FV%2Fw%3D)
SecureID Attributes
| Attribute | Description |
|---|---|
profiling.secure_id.identifier |
Derived Secure ID (from public key + creation time) |
profiling.secure_id.algorithm |
Signature algorithm (ECDSA) |
profiling.secure_id.creation_time |
When the key pair was first generated |
profiling.secure_id.public_key |
Public key from the pair |
profiling.secure_id.nonce |
Server-generated nonce that was signed |
profiling.secure_id.signature |
Digital signature of the nonce |
profiling.secure_id.storage |
Key storage location: indexedDB, KeyStore, or KeyChain |
profiling.secure_id.signals |
Verification outcome signals |
SecureID for Profiling Verification
| What to check | Attribute / Signal | Query |
|---|---|---|
| Secure ID value | profiling.secure_id.identifier |
Direct lookup |
| Missing Secure ID on valid blob | profiling.secure_id.identifier |
profiling.secure_id.identifier = null AND profiling.source != NULL |
| New device (first Secure ID) | NEW_KEY |
has(profiling.secure_id.signals, 'NEW_KEY') |
| Returning device | EXISTING_KEY |
has(profiling.secure_id.signals, 'EXISTING_KEY') |
| Nonce request failed | SECURE_ID_NONCE_REQUEST_FAILED |
has(profiling.secure_id.signals, 'SECURE_ID_NONCE_REQUEST_FAILED') |
Replay Detection
A replay attack re-sends a captured profiling blob to inherit the original device's fingerprint and risk score. We counter this with two mechanisms:
Nonce invalidation - The Secure ID nonce is single-use and automically deleted after verification. A replayed blob's nonce lookup fails, which triggers INVALID_NONCE and clears all Secure ID fields. The attacker inherits device fingerprint data for ease of associating with the session that it stole, but loses cryptographic device identity to prevent it being trusted.
Replay count - We track every profiling handle and count reuse. The profiling.trace_info.* attributes identify the original event.
It is OK to call collect() multiple times against the same DWN profiling object. This reissues the nonce. As long as only one profile blob is eventually sent to the Darwinium API from that object. profiling.handle should be unique on events.
| Attribute/Query | Description |
|---|---|
has(profiling.secure_id.signals, 'INVALID_NONCE') |
Nonce not valid format |
has(profiling.secure_id.signals, 'POSSIBLE_REPLAY') |
Nonce already consumed |
profiling.replay_count>0 |
Number of replays |
profiling.trace_info.timestamp |
Original event time |
profiling.trace_info.journey_name |
Journey that first used the blob |
profiling.trace_info.step_name |
Step that first used the blob |
profiling.trace_info.event_id |
Event ID of original request |