Skip to content

Signed URL Verification

The Edge Function verifies signed URLs to gate access to protected content. Two verification modes are available, chosen per deployment: CDN-native verification and custom HMAC verification.

Mode A: CDN-Native Verification (CloudFront, Akamai)

Section titled “Mode A: CDN-Native Verification (CloudFront, Akamai)”

The CDN platform handles signature verification at the infrastructure level, before the edge function code runs.

CloudFront implementation: Configure a trusted_key_groups reference on the cache behavior for /premium/*. Upload the RSA public key to CloudFront Key Management. CloudFront verifies the Signature, Key-Pair-Id, and Expires (or canned/custom Policy) parameters before the request reaches any function code.

Akamai implementation: Configure EdgeAuth token verification on the property manager for protected paths. The EdgeAuth token includes a hash, expiry, and optional IP binding. Verification happens at the property level.

The Edge Function does not verify the cryptographic signature in this mode. It only performs additional checks (agent binding, single-use) that the CDN cannot do natively.

Mode B: Custom HMAC Verification (Cloudflare, Fastly)

Section titled “Mode B: Custom HMAC Verification (Cloudflare, Fastly)”

The edge function code verifies the signature using HMAC-SHA256.

HMAC signed URL format (current implementation):

https://cdn.provider.com/premium/article.html?expires=1773451434&sig=a7f3b2c1...
  • expires — Unix timestamp (seconds since epoch).
  • sig — HMAC-SHA256 hex digest of {baseURL}{expires} using the shared secret.

Production enhancement — add delimiter and agent binding:

https://cdn.provider.com/premium/article.html
?expires=1773451434
&agent_id=sha256(LIC-BUYER-001)
&txn_id=txn-mp-93a7f2
&sig=HMAC-SHA256(baseURL\nexpires\nagent_id\ntxn_id, secret)
  • agent_id — SHA-256 hash of the agent’s license ID. The URL parameter agent_id carries the value from proto’s TransactionResponse.agent_identity_hash field (SHA-256 of the agent’s license ID).
  • txn_id — transaction ID for reconciliation and single-use enforcement.
  • \n delimiter between fields prevents canonicalization ambiguity (as noted in the threat model’s HMAC implementation note).

Both the Exchange (signer) and the edge function (verifier) MUST use the same canonical format for HMAC input. Fields are concatenated with \n delimiters in this fixed order:

baseURL\nexpires\nagent_id\ntxn_id

Where:

  • baseURL — the content URL without query parameters (e.g., https://cdn.example.com/premium/article.html)
  • expires — Unix timestamp in seconds (string representation)
  • agent_id — SHA-256 hex hash of the agent’s license ID (matches TransactionResponse.agent_identity_hash)
  • txn_id — the transaction ID (ULID)

All four fields are REQUIRED in production signed URLs. The agent_id field binds the URL to a specific agent (preventing sharing, Threat T8). The txn_id field enables three-sided reconciliation (CDN logs, Exchange transactions, Usage reports).

This canonicalization format MUST match the Exchange’s signing format. Any mismatch between signer and verifier will cause all signed URLs to be rejected.

Signed URLs include an expiry timestamp. The edge function enforces two checks:

  1. Expiry check — reject URLs where expires < current time.
  2. Max TTL check — reject URLs where expires is more than maxUrlTtlSeconds from the current time (prevents URLs signed too far in the future).

The default max TTL is 300 seconds (5 minutes). This limits the replay window even without single-use enforcement.

HMAC comparisons use constant-time comparison (XOR loop), never ===. This prevents timing side-channel attacks where an attacker could determine partial signature matches based on response timing.

OperationTime
HMAC-SHA256 computation (Web Crypto)0.1-0.5ms
Timing-safe comparison (64-byte hex)< 0.01ms

When agentBindingEnabled is true, the edge function validates that the requester matches the agent_id bound into the signed URL.

End-to-end agent identity flow:

  1. Agent calls ExecuteTransaction with aisystem.aisysuse.lid (license ID, e.g., LIC-BUYER-001).
  2. Exchange computes agent_id_hash = SHA256(license_id).
  3. Exchange embeds agent_id_hash in the signed URL: ?Signature=...&agent_id=abc123...
  4. Agent fetches the signed URL with header: X-Agent-License-Id: LIC-BUYER-001
  5. Edge function extracts agent_id from URL parameter, extracts license_id from X-Agent-License-Id header, computes SHA256(header_value), compares against URL parameter.
    • Match: serve content.
    • Mismatch: 403.

The agent does not need to know the hash — it just sends its license_id in a header. The Exchange and the edge function independently compute the same hash.

Identity verification methods:

MethodReliabilityNotes
X-Agent-License-Id request headerMediumAgent self-reports. Can be spoofed.
TLS client certificate CN or SANHighRequires mTLS setup. Not common yet.
OAuth2 access_token introspectionHighRequires network call. Expensive at edge.
IP address hashLowMulti-tenant IPs, NAT, proxies.

Recommended approach for v1: X-Agent-License-Id header with SHA-256 hash comparison against the agent_id URL parameter. This prevents casual URL sharing but does not prevent a determined attacker who knows the license ID.

Future: mTLS with agent certificates issued by a RAMP-recognized CA.

For providers wanting additional replay protection, the edge function can enforce single-use URLs via edge KV:

KV TTL: Set to maxUrlTtlSeconds + 60s (URL max TTL plus a safety buffer). After this period, the signed URL is expired anyway, so the KV entry can be evicted.

Single-use enforcement is best-effort. CDN edge KV stores are eventually consistent across locations. The primary replay protection is agent identity binding + short TTL (5 minutes). Authoritative deduplication happens during reconciliation via CDN access logs and Exchange transaction records.

CDN KV consistency reality:

PlatformKV MechanismConsistency ModelPropagation Delay
CloudFrontNo native KV; Lambda@Edge + DynamoDBPer-region, eventually consistent (DynamoDB Global Tables)Seconds across regions
Cloudflare Workers KVWorkers KVEventually consistent~60s propagation across PoPs
Cloudflare Durable ObjectsDurable ObjectsStrong consistencySingle-location (latency tradeoff)
Akamai EdgeKVEdgeKVEventually consistent~5-10s in-region
Fastly KV StoreKV StoreEventually consistentSeconds across PoPs

Recommendation: Do not enforce strict single-use at the edge. Instead, use:

  1. Agent identity binding — the signed URL is bound to a specific agent_id. Even if replayed, only the authorized agent can use it.
  2. Short TTL (5 minutes) — limits the replay window to a narrow period.
  3. Reconciliation (detect abuse after the fact) — the Exchange transaction log and CDN access logs contain txn_id for every request. Cross-reference to detect replays and take action.

This matches ad-tech’s approach to impression deduplication: real-time is best-effort, reconciliation is authoritative.

The edge function verifies, it does not sign.

Signing SchemeWho SignsWho VerifiesEdge Function Has
CloudFront RSA (asymmetric)Exchange (private key)CloudFront infra (public key)Nothing (CDN-native)
Akamai EdgeAuth (symmetric)Exchange (shared secret)Akamai infra (shared secret)Nothing (CDN-native)
HMAC-SHA256 (symmetric)Exchange (shared secret)Edge function (same secret)The shared secret
RSA/Ed25519 (asymmetric, custom)Exchange (private key)Edge function (public key)Public key only

Concern with HMAC: When using HMAC verification (Cloudflare, Fastly), the edge function must hold the shared secret. If the edge function is compromised, the attacker can forge signed URLs. Key rotation requires updating both the Exchange and the edge function simultaneously.

Recommendation: Use asymmetric signing (Ed25519 or RSA) when the CDN does not provide native signed URL verification.