The Invisible Threat Lurking in Your API
Imagine you’re sending money to a friend via a banking app. You tap "Send," and the payment goes through. But suddenly, a hacker intercepts and resends that same request — without your permission. Your money is sent twice. 😱
This isn't science fiction. It’s called a replay attack, and it’s one of the simplest yet most dangerous attacks on modern APIs.
Recently, while working on the authentication microservice of a supply chain finance application for a bank with strict security policies, we encountered a scenario during UAT where testers deliberately modified and resent previous requests. This real-world example highlighted the tangible risks of replay attacks and the importance of implementing robust security mechanisms.
But don’t worry. Nonces (number used once) are here to save the day. In this blog, you’ll learn how to shield your APIs from replay attacks, man-in-the-middle (MITM) attacks, and request tampering using nonces, Redis, and timestamp validation.
By the end of this post, you'll be able to build a fortress around your API that even the sneakiest hackers can’t break into. 🏰
📘 What is a Nonce?
A nonce (number used once) is a randomly generated unique or encrypted value attached to each request. It acts as a one-time identifier that ensures no two requests are identical, even if their contents are the same.
The key attributes of a nonce are:
- Unique: Each request has a distinct identifier.
- Time-Limited: Nonces are only valid for a certain time window (e.g., 3 days).
- Replay-Protected: Once a nonce is used, it cannot be reused.
By combining the power of nonces with Redis for storage and timestamp validation, we can create a system where each API request is thoroughly validated and immune to replay attacks.
📋 How Does This Protection Work?
The process to protect an API request using nonce can be broken down into the following steps:
- Generate Nonce (Client-Side)
The client creates a unique, encrypted token containing the following information:
- Unique Identifier (UUID) or only Timestamp
-
Timestamp (to track when the request was created)
- Send Nonce in the Request
The client sends the nonce in the request header (e.g., X-Request-Token
) when calling the API.
- Decrypt Token (Server-Side)
The server decrypts the token and extracts the unique identifier and timestamp.
- Timestamp Validation
The server checks if the time difference between the current server time and the client's timestamp is within an acceptable window (e.g., less than 3 days). If the request nonce timestamp is too old, it is rejected.
- Replay Protection (Using Redis)
The server queries Redis to check if the unique identifier has been used before.
- If the nonce exists in Redis, the request is rejected.
- If the nonce does not exist, it is stored in Redis with a time-to-live (TTL) of 3 days.
- Pass Request to the Next Step
If all checks pass, the request is processed. The nonce is stored in Redis to ensure it cannot be used again.
📦 Key Components of the System
To build a complete nonce-based protection system, you need the following components:
1️⃣ Token Encryption
The nonce (unique identifier + timestamp) is encrypted using AES (Advanced Encryption Standard) or any other secure encryption method. This ensures the token cannot be easily manipulated. Only the server knows how to decrypt the token.
2️⃣ Timestamp Validation
When the server receives the request, it extracts the timestamp and checks how much time has passed. If the timestamp is older than the allowed time window (e.g., 3 days), the request is rejected. This prevents attackers from replaying old requests.
3️⃣ Replay Protection (Redis)
Redis is a fast, in-memory data store that can efficiently track used nonces. Every nonce is stored in Redis with a 3-day TTL (time-to-live). If a request with the same nonce is received, the server immediately rejects it. Redis's TTL feature ensures nonces expire automatically after 3 days.
Diagram
💻 Implementation (Let's build it )
Client-Side: Generating and Sending the Nonce
The client generates a unique nonce, encrypts it, and sends it as a header in the HTTP request. This ensures each request is unique and protects against replay attacks.
Explanation of Client Code:
- Generate Nonce: Combines the current timestamp with some randomness and encrypts it.
- Encryption: Uses AES or a similar method to encrypt the nonce, making it tamper-proof.
- Attach to Request: Sends the nonce in the X-Request-Token header, which the server will validate
Server-Side: Validating the Nonce
On the server side, the encrypted nonce is decrypted, the timestamp is validated, and Redis ensures the nonce hasn’t been used before.
Explanation of Server Code:
- Extract and Decrypt: The server retrieves the nonce from the X-Request-Token header and decrypts it.
- Timestamp Validation: Validates that the request timestamp is within the allowed time window (e.g., 3 days).
- Replay Protection with Redis: The server checks Redis to ensure the nonce hasn’t been used before. If it has, the request is rejected.
- Error Handling: If the nonce is invalid or expired, the request is rejected with an error message.
🔥 Benefits of Nonce-Based Protection
- Prevents Replay Attacks: Nonces ensure every request is unique and cannot be reused.
- Time-Limited Tokens: The token's expiration (default 3 days) limits its usefulness.
- Efficient Storage (Redis): Redis’s in-memory data store ensures fast lookup and storage of nonces.
- Tamper-Proof Requests: Encrypted tokens ensure attackers cannot modify the request data.
- Stateless Validation: The use of Redis eliminates the need to maintain request state on the server.
🕵️♂️ Example Scenario
Imagine you have an online payment API that processes orders. Without nonce protection, an attacker could intercept and replay a "Purchase Order" request. This could cause multiple, unauthorized orders to be placed.
With nonce protection, the steps look like this:
- The client creates a token with a ** timestamp **.
- The server decrypts the token, validates the timestamp, and checks Redis for the nonce.
- If everything is valid, the server processes the order.
- The nonce is saved in Redis, so it cannot be used again.
Result: If an attacker tries to replay the purchase request, the server will detect that the nonce has already been used and reject it.
Acknowledgment: This post combines insights from AI and personal experience.
Top comments (0)