Enhanced request signing with domain verification (v1.1)#220
Open
Enhanced request signing with domain verification (v1.1)#220
Conversation
Implements cryptographic signing of OpenRTB requests that includes publisher domain verification and replay protection. The signed payload now includes: - Key ID (kid) - Request host - Request scheme - Request ID - Unix timestamp This prevents request tampering and domain spoofing by ensuring the signature is bound to the originating publisher domain. Changes: - Add version and ts fields to TrustedServerExt - Add SigningParams struct and sign_request() method - Update PrebidAuctionProvider to use enhanced signing - Add comprehensive tests for payload construction and signing
ChristianPavilonis
requested changes
Feb 3, 2026
Collaborator
There was a problem hiding this comment.
pretty good, a couple nit picks.
Opening an issue on mocktioneer to verify new signature payload: stackpop/mocktioneer#30
deployed test site with this (verification broken until mocktioneer updated) but the TS side is working.
| request_scheme, | ||
| timestamp: std::time::SystemTime::now() | ||
| .duration_since(std::time::UNIX_EPOCH) | ||
| .map(|d| d.as_secs()) |
Collaborator
There was a problem hiding this comment.
Should use milliseconds to stay in line with the rest of the openrtb spec.
| let signature = signer.sign(id.as_bytes())?; | ||
| let params = SigningParams::new( | ||
| id.to_string(), | ||
| request_host.clone(), |
Collaborator
There was a problem hiding this comment.
You don't have to clone here if you use params.request_host and params.request_scheme on lines 544-545
Collaborator
Author
Enhanced Request Signing (v1.1) - Architecture DiagramRequest Signing FlowsequenceDiagram
participant Client as Publisher<br/>Website
participant TS as Trusted Server
participant Signer as Request<br/>Signer
participant SSP as Prebid SSP/<br/>Ad Exchange
Client->>TS: Ad Request
Note over TS: Extract request metadata
TS->>TS: Parse request_host<br/>(e.g., "publisher.com")
TS->>TS: Parse request_scheme<br/>(e.g., "https")
TS->>TS: Generate request_id<br/>(e.g., "auction-123")
TS->>Signer: Create SigningParams
Note over Signer: SigningParams {<br/> request_id: "auction-123"<br/> request_host: "publisher.com"<br/> request_scheme: "https"<br/> timestamp: 1738527600<br/>}
Signer->>Signer: Build canonical payload
Note over Signer: Format:<br/>"kid:host:scheme:id:ts"<br/><br/>Example:<br/>"ts-2026-01-A:publisher.com:https:auction-123:1738527600"
Signer->>Signer: Sign with Ed25519 private key
Signer->>Signer: Base64url encode signature
Signer-->>TS: Return signature
TS->>TS: Build OpenRTB request with ext.trusted_server
Note over TS: {<br/> "version": "1.1",<br/> "kid": "ts-2026-01-A",<br/> "request_host": "publisher.com",<br/> "request_scheme": "https",<br/> "ts": 1738527600,<br/> "signature": "base64..."<br/>}
TS->>SSP: POST /openrtb2/auction
SSP->>SSP: Verify signature
Note over SSP: 1. Reconstruct payload<br/>2. Fetch public key by kid<br/>3. Verify Ed25519 signature<br/>4. Check timestamp freshness<br/>5. Validate domain binding
alt Valid Signature
SSP-->>TS: 200 OK with bids
TS-->>Client: Return ad
else Invalid Signature
SSP-->>TS: 403 Forbidden
Note over SSP: Reject if:<br/>• Signature invalid<br/>• Timestamp too old<br/>• Domain mismatch<br/>• Missing fields
TS-->>Client: Error response
end
Security Propertiesflowchart TB
subgraph Input["Request Inputs"]
A1[Request ID]
A2[Publisher Host]
A3[Request Scheme]
A4[Timestamp]
A5[Key ID]
end
subgraph Signing["Canonical Payload Construction"]
B1["Build payload string:<br/>kid:host:scheme:id:ts"]
end
subgraph Crypto["Cryptographic Signing"]
C1[Ed25519 Private Key]
C2[Sign payload]
C3[Base64url encode]
end
subgraph Output["Signed Request Extension"]
D1[version: '1.1']
D2[kid]
D3[request_host]
D4[request_scheme]
D5[ts]
D6[signature]
end
subgraph Protection["Security Guarantees"]
E1[Domain Binding]
E2[Replay Protection]
E3[Tampering Detection]
E4[Request Authenticity]
end
Input --> Signing
Signing --> Crypto
C1 --> C2
C2 --> C3
Crypto --> Output
Output --> Protection
Attack Preventionflowchart LR
subgraph Threats["Attack Vectors"]
T1[Domain Spoofing]
T2[Request Replay]
T3[Payload Tampering]
T4[MITM Attacks]
end
subgraph v10["v1.0 Signing<br/>(Only ID)"]
V1[❌ Vulnerable to<br/>domain spoofing]
V2[❌ No replay<br/>protection]
V3[✅ Tampering<br/>detection]
end
subgraph v11["v1.1 Enhanced Signing<br/>(ID + Host + Scheme + Timestamp)"]
N1[✅ Domain binding<br/>prevents spoofing]
N2[✅ Timestamp prevents<br/>replay attacks]
N3[✅ Enhanced tampering<br/>detection]
N4[✅ Scheme validation<br/>prevents downgrade]
end
T1 --> V1
T1 --> N1
T2 --> V2
T2 --> N2
T3 --> V3
T3 --> N3
T4 --> N4
Payload Format EvolutionVersion 1.0 (Legacy)Version 1.1 (Enhanced)Verification Process (SSP/Exchange Side)flowchart TD
Start([Receive OpenRTB Request]) --> Extract[Extract ext.trusted_server]
Extract --> CheckVersion{version == '1.1'?}
CheckVersion -->|No| Legacy[Use legacy verification]
CheckVersion -->|Yes| CheckFields{All fields present?}
CheckFields -->|Missing| Reject1[❌ Reject: Missing fields]
CheckFields -->|Present| BuildPayload[Reconstruct payload:<br/>kid:host:scheme:id:ts]
BuildPayload --> FetchKey[Fetch Ed25519 public key<br/>using kid]
FetchKey --> KeyFound{Key exists?}
KeyFound -->|No| Reject2[❌ Reject: Unknown key]
KeyFound -->|Yes| VerifySig[Verify Ed25519 signature]
VerifySig --> SigValid{Signature valid?}
SigValid -->|No| Reject3[❌ Reject: Invalid signature]
SigValid -->|Yes| CheckTime[Check timestamp freshness]
CheckTime --> TimeValid{ts within window?<br/>e.g., ±5 minutes}
TimeValid -->|No| Reject4[❌ Reject: Timestamp too old/new]
TimeValid -->|Yes| CheckDomain{request_host matches<br/>expected publisher?}
CheckDomain -->|No| Reject5[❌ Reject: Domain mismatch]
CheckDomain -->|Yes| CheckScheme{request_scheme == 'https'?}
CheckScheme -->|No| Reject6[❌ Reject: Invalid scheme]
CheckScheme -->|Yes| Accept[✅ Accept Request]
Reject1 --> End([Return 403])
Reject2 --> End
Reject3 --> End
Reject4 --> End
Reject5 --> End
Reject6 --> End
Accept --> Process[Process auction]
Key Benefits
Implementation Notes
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
kid:request_host:request_scheme:id:tsversion("1.1") andts(Unix timestamp) fields toext.trusted_serverThis prevents request tampering and domain spoofing by ensuring the signature is cryptographically bound to the originating publisher domain.
Changes
versionandtsfields toTrustedServerExtSigningParamsstruct,SIGNING_VERSIONconstant, andsign_request()methodPrebidAuctionProviderandenhance_openrtb_requestto use enhanced signingOutput Format
{ "ext": { "trusted_server": { "version": "1.1", "kid": "ts-2026-01-A", "request_host": "publisher.com", "request_scheme": "https", "ts": 1738527600, "signature": "base64-encoded-ed25519-signature" } } }Test plan
cargo build --release)cargo clippy)