Skip to content

Proof Of Work Mechanism

The proof-of-work mechanism is designed to prevent automated abuse and spam by requiring website visitors to perform a computational task. This task involves finding a specific number determined by the server, and utilizing SHA hashing for challenge generation and verification. The system employs a randomized salt and secret number to create a challenge that users must solve to submit the form or access the website.

Challenge Generation

  1. Random salt creation
    A server generates a random string (salt) of sufficient length (usually at least 10 characters). This salt serves as an additional input to ensure the uniqueness and complexity of the challenge for each user.

  2. Secret number generation
    The server generates a secret number, a positive integer, which remains hidden from the client. This number determines the complexity of the challenge.

  3. Challenge computation
    The server concatenates the salt and secret number, creating a unique string. This concatenated string is hashed using the SHA algorithm, producing a fixed-size hash, representing the challenge.

  4. Server signature creation
    An HMAC key is used to create a signature based on the challenge. This signature acts as a verification mechanism for the correctness of the solution submitted by the user.

In this proof-of-work mechanism, the client’s task involves iterative computation to find a matching solution for the challenge provided by the server.

Client-side Task

The client’s task involves continuously iterating through numbers, combining them with the provided salt, and hashing them using SHA until discovering a number that, when hashed with the salt, matches the challenge received from the server. This iterative process requires computational effort from the client’s side to find a solution that meets the criteria set by the server.

Upon finding a matching solution, the client submits the solution along with the original challenge back to the server for validation. If the submitted solution aligns with the original challenge, as per the server’s verification process, the client gains access to the website’s services, demonstrating that it has completed the proof-of-work task successfully.

  1. Iterative computation

    • Starting from zero, the client iterates through numbers, incrementing sequentially.
    • For each iteration, it concatenates the salt received from the server with the current number being tested.
    • It applies the SHA hashing algorithm to the concatenated string.
    • The resulting hash is compared to the challenge received from the server.
  2. Matching solution
    The client continues this iterative process until it finds a number that, when combined with the provided salt, produces a SHA hash that matches the challenge provided by the server.

Solution Verification

  1. Challenge validation
    Using the received salt and number from the client, the server re-computes the challenge by concatenating these values and applying the SHA hashing algorithm. It then compares this computed challenge with the one received from the client to confirm their equality.

  2. Signature validation
    The server reconstructs the signature based on the challenge and HMAC key. It compares this reconstructed signature with the one submitted by the user to authenticate the correctness of the solution.

  3. Verification check
    The request is considered verified only if both validation checks (challenge and signature) pass. If successful, the server grants access or processes the user request; otherwise, it denies access or treats the request as potentially malicious.

Pseudo Code

Creating a Challenge

Below is a pseudo-code example for creating a challenge on the server:

// Choose the complexity - the maximum random number (refer to complexity documentation)
maxnumber = 100_000;
// Generate a random salt (recommended length: at least 10 characters)
salt = random_string();
// Generate a random secret number (positive integer)
// Range between 0...maxnumber
// Ensure NOT to expose this number to the client
secret_number = random_int(maxnumber);
// Compute the SHA-256 hash of the concatenated salt + secret_number (result encoded as HEX string)
challenge = sha2_hex(concat(salt, secret_number));
// Create a server signature using an HMAC key (result encoded as HEX string)
signature = hmac_sha2_hex(challenge, hmac_key);
// Return JSON-encoded data
response = {
algorithm: 'SHA-256',
challenge,
maxnumber,
salt,
signature,
};

When the parameter maxnumber is returned to the client, the ALTCHA widget can better optimize the workload distribution between multiple workers, making it run faster. The official libraries return the maxnumber value by default as it is generally recommended. Depending on your use case, the parameter maxnumber could be considered a secret parameter, which should not be exposed to the client as it reveals the range of the number. In specific cases where you intentionally want a high difficulty, such as M2M or custom integrations, don’t return maxnumber.

Salt Parameters

It is recommended to include the expires flag and additional parameters in the salt as URL-encoded query strings. This allows you to pass custom data, which will be part of the signature and verifiable on the server.

The widget automatically detects the expires parameter when provided in the salt as a Unix timestamp in seconds:

salt = '<random_salt>?expires=1715526540'

To ensure compatibility with future versions of the widget, it’s recommended to prefix any custom parameters with an underscore _.

Solution Validation

Here is a pseudo-code example for validating a solution on the server:

// The payload is a BASE64-JSON-encoded string
// The decoded data is an object containing { algorithm, challenge, number, salt, signature }
data = json_decode(base64_decode(payload));
// Validate algorithm
alg_ok = equals(data.algorithm, 'SHA-256');
// Validate challenge
challenge_ok = equals(data.challenge, sha2_hex(concat(data.salt, data.number)))
// Validate signature
signature_ok = equals(data.signature, hmac_sha2_hex(data.challenge, hmac_key))
// Consider the request verified if all checks are true
verified = alg_ok and challenge_ok and signature_ok