-
Notifications
You must be signed in to change notification settings - Fork 34
Open
Description
Problem
Security vulnerability: Signatures never expire. An attacker who intercepts a valid payment signature can replay it hours or days later to drain funds or abuse the service.
Current Flow (Vulnerable)
sequenceDiagram
participant A as Attacker
participant V as Victim
participant G as Gateway
V->>G: Valid request (signature)
A->>A: Intercept signature
Note over A: Wait 1 hour...
A->>G: Replay same signature
G-->>A: Works! (Bug)
Why This Matters
- Man-in-the-middle attacks - Attacker intercepts network traffic
- Stolen credentials - If signature is logged or leaked
- Session hijacking - Reuse old valid signatures
Solution
Add a timestamp field to the EIP-712 payment message and reject signatures older than a configurable window (default: 5 minutes).
Protected Flow
sequenceDiagram
participant A as Attacker
participant G as Gateway
A->>G: Replay old signature
Note over G: Check timestamp
Note over G: age = now - timestamp
Note over G: age > 5 min
G-->>A: 400 E007 Signature Expired
Technical Details
Modified EIP-712 Structure
The payment context must include a timestamp field. This is a breaking change - all clients must update.
// Before
struct PaymentContext {
recipient: Address,
amount: String,
token: String,
nonce: String,
chain_id: u64,
}
// After
struct PaymentContext {
recipient: Address,
amount: String,
token: String,
nonce: String,
chain_id: u64,
timestamp: u64, // Unix timestamp in seconds
}Validation Logic
flowchart TD
A[Signature + Timestamp] --> B{timestamp > now + 60s?}
B -->|Yes| C["E008: Future timestamp"]
B -->|No| D{age > max_window?}
D -->|Yes| E["E007: Signature expired"]
D -->|No| F["Valid"]
style C fill:#f96
style E fill:#f96
style F fill:#6f6
Edge Cases to Handle
| Scenario | Expected Behavior |
|---|---|
| Signature from 2 minutes ago | Valid |
| Signature from 4 minutes ago | Valid (within 5 min window) |
| Signature from 10 minutes ago | Reject with E007 |
| Signature with future timestamp (+2 min) | Reject with E008 |
| Missing timestamp field | Reject with E009 |
| Client clock 30 seconds ahead | Allow (60s grace period) |
Implementation
Rust Verifier
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug)]
enum VerifyError {
SignatureExpired { age_seconds: u64, max_seconds: u64 },
FutureTimestamp { timestamp: u64, now: u64 },
MissingTimestamp,
}
fn validate_timestamp(timestamp: u64, window_seconds: u64) -> Result<(), VerifyError> {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
// Reject future timestamps (with 60s grace for clock skew)
if timestamp > now + 60 {
return Err(VerifyError::FutureTimestamp { timestamp, now });
}
// Reject expired signatures
let age = now.saturating_sub(timestamp);
if age > window_seconds {
return Err(VerifyError::SignatureExpired {
age_seconds: age,
max_seconds: window_seconds
});
}
Ok(())
}Go Gateway Changes
The gateway must include timestamp when generating the payment context:
// In handleSummarize
paymentContext := PaymentContext{
Recipient: getRecipientAddress(),
Token: "USDC",
Amount: getPaymentAmount(),
Nonce: uuid.New().String(),
ChainID: getChainID(),
Timestamp: uint64(time.Now().Unix()), // Add this
}Acceptance Criteria
- Add
timestampfield to EIP-712 PaymentContext - Validate timestamp in Rust verifier
- Configurable expiry window via
SIGNATURE_EXPIRY_SECONDS - Return E007 for expired signatures
- Return E008 for future timestamps
- Update Go gateway to include timestamp
- Add tests for all edge cases (expired, future, boundary)
- Document breaking change in CHANGELOG
- Update TypeScript client SDK (if exists)
Environment Variables
# Signature expiry window in seconds (default: 300 = 5 minutes)
SIGNATURE_EXPIRY_SECONDS=300
# Grace period for clock skew (default: 60 seconds)
SIGNATURE_CLOCK_SKEW_SECONDS=60Testing
cd verifier && cargo test
# Manual testing scenarios:
# 1. Fresh signature (now) -> Should pass
# 2. Signature from 3 min ago -> Should pass
# 3. Signature from 10 min ago -> Should fail E007
# 4. Signature with timestamp +5 min -> Should fail E008Migration Guide
This is a breaking change. Existing clients must update:
- Update client SDK to include
timestampin payment context - Regenerate signatures with new EIP-712 domain
- Deploy updated verifier before clients (with fallback period)
Reactions are currently unavailable