In this lesson, you will learn how to implement request signing to ensure that the data sent to your API hasn't been tampered with during transit.
Why Sign Requests?
While HTTPS encrypts the connection, it doesn't prove that the specific body of a request wasn't modified by an intermediary if the transport layer is compromised. Request signing involves the client creating a digital "fingerprint" of the request payload using a secret key known only to the client and the server. The server then re-calculates this signature; if it matches, the request is authentic and intact.
Implementation Strategy
The process generally involves creating a canonical string (a sorted, predictable representation of the request data) and hashing it with a secret key using an algorithm like HMAC-SHA256.
In your Node.js middleware, you verify the signature by fetching the client's secret key from your database and performing the same hash calculation:
const crypto = require('crypto');
function verifySignature(req, secretKey) {
const signature = req.headers['x-signature'];
const timestamp = req.headers['x-timestamp'];
const payload = JSON.stringify(req.body);
// Re-create the signature using the secret key and the request body
const expectedSignature = crypto
.createHmac('sha256', secretKey)
.update(timestamp + payload)
.digest('hex');
// Use timingSafeEqual to prevent side-channel attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
Critical Considerations
- Include a Timestamp: Always include a timestamp in the signed data. This prevents "replay attacks," where an attacker captures a valid signed request and sends it repeatedly. The server should reject any request signed more than a few minutes ago.
- Canonicalization: Ensure both the client and server agree on the exact format of the data. If the client sends
{"a": 1, "b": 2}but the server processes it as{"b":2,"a":1}, the signatures will never match. Sorting object keys before signing is a common industry standard to avoid this issue. - Key Management: Never store these keys in plaintext. Since you have previously studied Using Hardware Security Modules (HSM), consider using those to store the server-side signing keys for maximum security.
Key Takeaway: Request signing guarantees data integrity and authenticity by combining a secret key with request metadata to create a verifiable cryptographic bond between the sender and the server.