V3 architecture Encryption Mode

更新时间:
复制 MD 格式

Encryption Mode adds a server-side layer of protection to your CAPTCHA integration. Instead of passing the sceneId directly to the frontend, your server encrypts it into a short-lived token (EncryptedSceneId) that the CAPTCHA service validates on every request. This prevents abuse from a leaked sceneId.

Encryption Mode applies to CAPTCHA V3 architecture only. V2 architecture does not require it because verification is initiated server-side.

How it works

  1. Server encrypts the scene ID. Your server uses the Encrypt Key (ekey) from the console to produce a time-limited encrypted string (EncryptedSceneId). The plaintext encodes the sceneId, the current timestamp, and a validity period.

  2. Frontend passes the token. The frontend fetches EncryptedSceneId from your server and passes it to initAliyunCaptcha.

  3. CAPTCHA service validates the token. After Encryption Mode is enabled in the console, the CAPTCHA server decrypts and checks every request that includes EncryptedSceneId. Requests that fail any check are rejected.

Prerequisites

Before you begin, ensure that you have:

Step 1: Generate an encrypted string on the server

Get the Encrypt Key (ekey)

Log in to the Alibaba Cloud Captcha console. In the upper-right area of the Overview page, copy the Encrypt Key (ekey).

Construct and encrypt the plaintext

1. Build the plaintext string.

Concatenate the three fields with & as the delimiter:

sceneId&timestamp&expireTime
FieldTypeDescription
sceneIdStringUnique identifier for the CAPTCHA scenario. Formerly known as CaptchaAppid.
timestampLongCurrent UNIX timestamp in seconds. Must not be later than the time the server receives the request.
expireTimeIntValidity period in seconds. Accepted range: 186400 (1 second to 24 hours).

Example plaintext:

sdewfwe&1712345678&3600

2. Encrypt with AES-256-CBC.

Apply AES encryption to the plaintext string using the following parameters:

ParameterValue
AlgorithmAES (Advanced Encryption Standard)-256
ModeCBC (Cipher Block Chaining)
PaddingPKCS7Padding
KeyThe Encrypt Key (ekey) from the console
Initialization Vector (IV)A randomly generated 16-byte array

The output is an encrypted byte array (CaptchaSceneIdEncrypted).

Important

Generate a new IV using a cryptographically secure random number generator for every encryption call. Never reuse IVs.

3. Concatenate and Base64-encode.

  1. Concatenate the 16-byte IV and CaptchaSceneIdEncrypted in order, with no separator: IV + CaptchaSceneIdEncrypted.

  2. Base64-encode the concatenated byte array using standard encoding without line feeds.

The resulting string is EncryptedSceneId.

Java code example

The following example generates an EncryptedSceneId using AES-256-CBC encryption.

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Base64;

public class SceneIdEncryptor {

    // The IV is a fixed length of 16 bytes (AES block size).
    private static final int IV_LENGTH_BYTES = 16;

    /**
     * Encrypts the sceneId to generate an EncryptedSceneId.
     *
     * @param sceneId        The CAPTCHA scenario identifier (formerly CaptchaAppid).
     * @param ekeyStr        The ekey from the console (used as the encryption key).
     * @param expireTimeSec  The validity period in seconds. Must be between 1 and 86400.
     * @return A Base64-encoded string: EncryptedSceneId (IV + encrypted data).
     * @throws Exception     Thrown for unsupported algorithms or other encryption errors.
     */
    public static String encryptSceneId(String sceneId, String ekeyStr, int expireTimeSec) throws Exception {
        if (expireTimeSec <= 0 || expireTimeSec > 86400) {
            throw new IllegalArgumentException("expireTimeSec must be between 1 and 86400 seconds.");
        }

        // Get the current timestamp in seconds.
        long timestamp = Instant.now().getEpochSecond();

        // Step 1: Decode the ekey into a 32-byte key.
        byte[] keyBytes = Base64.getDecoder().decode(ekeyStr);

        // Step 2: Build the plaintext and encrypt with AES-256-CBC.
        // Plaintext format: sceneId&timestamp&expireTime
        String plaintext = sceneId + "&" + timestamp + "&" + expireTimeSec;
        byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);

        // Java uses PKCS5Padding, which is equivalent to PKCS7Padding for AES.
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        // Generate a cryptographically secure random 16-byte IV.
        SecureRandom random = new SecureRandom();
        byte[] iv = new byte[IV_LENGTH_BYTES];
        random.nextBytes(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);

        byte[] encryptedBytes = cipher.doFinal(plaintextBytes);

        // Step 3: Concatenate IV + ciphertext, then Base64-encode.
        byte[] result = new byte[IV_LENGTH_BYTES + encryptedBytes.length];
        System.arraycopy(iv, 0, result, 0, IV_LENGTH_BYTES);
        System.arraycopy(encryptedBytes, 0, result, IV_LENGTH_BYTES, encryptedBytes.length);

        return Base64.getEncoder().encodeToString(result);
    }

    public static void main(String[] args) {
        try {
            String sceneId = "s123456789";       // Replace with your actual sceneId.
            String ekey = "<your-ekey>";         // Replace with the ekey from the console.
            int expireTimeSec = 3600;            // Set the desired validity period.

            String encryptedSceneId = encryptSceneId(sceneId, ekey, expireTimeSec);
            System.out.println("EncryptedSceneId: " + encryptedSceneId);
            // Output differs on each run because the IV is randomly generated.
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Replace the following placeholder before running the example:

PlaceholderDescription
<your-ekey>The Encrypt Key (ekey) from the Alibaba Cloud Captcha console

Step 2: Pass the encrypted string from the frontend

Before initializing the CAPTCHA, fetch EncryptedSceneId from your server and pass it as a parameter to initAliyunCaptcha.

EncryptedSceneId parameter

ParameterTypeRequiredDefaultDescription
EncryptedSceneIdStringNoNoneThe encrypted scene ID generated by your server.

JavaScript code example

// Fetch the EncryptedSceneId from your backend.
const getEncryptedSceneId = async () => {
  const response = await fetch('/api/encrypt-scene-id', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
  });
  // Backend returns JSON: { "EncryptedSceneId": "..." }
  const { EncryptedSceneId } = await response.json();
  return EncryptedSceneId;
};

// Initialize the CAPTCHA with the encrypted scene ID.
const initCaptcha = async () => {
  const encryptedId = await getEncryptedSceneId();
  window.initAliyunCaptcha({
    SceneId: '<your-scene-id>',        // The scene ID from the Captcha console.
    EncryptedSceneId: encryptedId,
    // ... other initialization parameters
  });
};

initCaptcha();

Replace the following placeholder before using this example:

PlaceholderDescription
<your-scene-id>The scene ID from the scenario list in the Alibaba Cloud Captcha console

Step 3: Enable Encryption Mode in the console

After you develop and test the frontend and backend code, you can enable Encryption Mode for the corresponding scenario in the Captcha console.

  1. Log in to the Alibaba Cloud Captcha console. In the left navigation pane, click Scenarios.

  2. On the Scenarios page, find the target scenario and turn on the switch in the Encryption Mode column.

After enabling Encryption Mode, the CAPTCHA server performs the following mandatory checks on all requests that include an EncryptedSceneId:

  • Decryption succeeds using the registered ekey.

  • The sceneId is valid.

  • The timestamp falls within the allowed time window (prevents replay attacks).

  • The encrypted string is within the validity period defined by expireTime.

Any request that fails a check is rejected.

Before you enable this switch, ensure that your server-side encryption logic and frontend integration are correctly implemented and fully tested.

Security checklist

Before going live with Encryption Mode, verify the following:

  • [ ] Generate a fresh IV per request. Never reuse initialization vectors. Use SecureRandom (Java) or an equivalent cryptographically secure generator in your language.

  • [ ] Keep the ekey secret. Store it as an environment variable or in a secrets manager. Never expose it in client-side code.

  • [ ] Set an appropriate validity period. Keep expireTime as short as practical for your use case to limit the exposure window if a token is intercepted.

  • [ ] Test before enabling. Verify that your encryption logic produces valid tokens before turning on the console switch.

  • [ ] Monitor for rejections. After enabling, watch for unexpected request rejections that may indicate a configuration mismatch between your server and the CAPTCHA service.