Documentation Index

Fetch the complete documentation index at: https://docs.darwinium.com/llms.txt

Use this file to discover all available pages before exploring further.

Profiling Tamper Prevention

Prev Next

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).

Image

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.
Image

CORS: If enforced, dwn-profiling must 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

  1. On first visit, the JS or Mobile SDK generates an ECDSA key pair on the device
  2. Keys are stored persistently: IndexedDB (web), KeyStore (Android), KeyChain (iOS)
  3. Our server provides a unique, single-use nonce
  4. The client signs the nonce with the private key
  5. The signed nonce, public key, and metadata are included in the blob
  6. Server-side: we verify the signature, derive the Secure ID from public key + creation time
  7. The nonce is permanently and atomically marked and cannot be reused
    Image

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.

Calling collect() multiple times

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