How to Secure Web Requests with Nonces
Issue
A user has found a way to exploit the request validation system of a website's scoring system by duplicating high-value HTTP requests. This compromises the integrity and reliability of the system.
Solution: Implementing a Nonce System
Nonces (number used once) are values that prevent request replay attacks by ensuring that a particular request has not been made before. Here's a common and secure way to implement a nonce system:
Server-Side Nonce Generation and Verification
getNonce() Function
- Identifies the client making the request (e.g., by username, session).
- Generates a random nonce using a secure hash function (e.g., SHA512).
- Stores the nonce in a database associated with the client's ID.
- Returns the nonce to the client.
verifyNonce() Function
- Fetches the previously stored nonce for the client ID.
- Removes the nonce from the database (to prevent it from being reused).
- Generates a hash with the client-provided nonce (cnonce), the request data, and a secret salt.
- Compares the generated hash to the hash provided by the client.
- Returns true if the hashes match, indicating a valid nonce.
Client-Side Nonce Usage
sendData() Function
- Retrieves the nonce from the server using the getNonce() function.
- Generates a client-specific nonce (cnonce) using a secure hash function.
- Concatenates the server nonce, client nonce, and request data.
- Generates a hash from the concatenated value.
- Sends the request to the server, including the data, cnonce, and hash.
Security Considerations
- Random Nonce Generation: The makeRandomString() function should generate highly unpredictable random numbers to enhance security.
- Secure Hash Function: Utilize a strong hash function like SHA512 or bcrypt for nonce-related hash computations.
- Single-Use Per Request: Nonces should only be used once and removed from storage to prevent replay attacks.