npm altcha-lib 2.6.0
v2.6.0

6 hours ago

This is a security and hardening release, and upgrading is recommended for all v2 users. It fixes a proof-of-work bypass in deterministic challenges, two replay-protection flaws in the framework plugins, and a key leak in obfuscate(). Invalid options and inputs now produce clear errors instead of being accepted silently.

Compatibility: challenges and obfuscated strings created with v2.5.0 still verify and decode after upgrading. With the default settings (PBKDF2 or SHA-256, keyLength 32), the wire format is unchanged. Some invalid configurations that used to be accepted now throw; see Upgrade notes.

Security fixes

  • Proof-of-work bypass in deterministic challenges. With counter and hmacKeySignatureSecret set and a keyPrefixLength at least as large as the key, the published keyPrefix was the whole derived key. A client could submit it as the solution without doing any work. keyPrefixLength must now be smaller than the derived key and is capped at half of it.
  • Challenges solvable with no work. An empty keyPrefix (from keyLength: 1, keyPrefixLength: 0 or keyPrefix: '') matched every key. These configurations now throw.
  • Replay of Sentinel payloads. The framework plugins keyed replay protection for server-signed payloads on the unsigned top-level id. Changing or removing it allowed the same payload to be replayed. The key is now the id inside the signed verificationData.
  • Replay-store eviction. Payload ids were written to the replay store before verification. Unverified requests could evict used ids from a CappedMap and so allow replays, fill a Redis store, or mark another user's challenge as used. Ids are now recorded only after successful verification.
  • Concurrent replays. A store can now implement setIfAbsent so that only one of two simultaneous submissions of the same payload passes. CappedMap implements it.
  • Key leak in obfuscate(). Passing keyPrefixLength in the options published the entire AES key, so the data could be decrypted without solving the challenge.
  • Unvalidated payload.algorithm. verifyServerSignature now accepts only SHA-1, SHA-256, SHA-384 and SHA-512, before any hashing.
  • __proto__ injection. A __proto__ key added to challenge parameters was not covered by the signature. It now invalidates the signature.

Other fixes

  • The browser SHA key derivation (altcha-lib/algorithms/web/sha) now produces the same keys as the Node version for SHA-384 and SHA-512 with cost > 1, and for any keyLength below the digest size.
  • The browser PBKDF2 key derivation (altcha-lib/algorithms/web/pbkdf2) supports any keyLength.
  • verifySolution returns invalidSolution instead of throwing or coercing in these cases:
    • a missing or malformed derivedKey, including one in uppercase;
    • a counter that is not an exact integer in range, such as "42" or 2 ** 32 + 42.
  • solveChallenge throws immediately for an invalid keyPrefix instead of searching until the timeout.
  • An odd-length uppercase keyPrefix from another issuer can now be solved and verified.
  • verifyServerSignature returns invalidSignature for a missing signature. Expiry is checked to the exact second, without the previous 1-second grace.
  • CLI: create --algorithm is case-insensitive and rejects unknown names, and it defaults to PBKDF2/SHA-256 as documented.

Upgrade notes

Most deployments need no changes. Check the following if they apply to you:

  • Option validation. createChallenge throws for invalid options. These are:

    • keyPrefixLength and keyPrefix outside the rules above;
    • a non-hex keyPrefix;
    • an out-of-range counter;
    • a non-positive or non-integer cost, keyLength, memoryCost or parallelism;
    • an unknown counterMode;
    • an invalid expiresAt;
    • an unsupported hmacAlgorithm.

    SHA-256 with keyLength: 64 and the default keyPrefixLength now throws in counter mode, because the derived key is only 32 bytes. A keyPrefix you pass in is lowercased before signing.

  • Secrets.

    • createChallenge throws for an empty or null hmacSignatureSecret. Omitting it still creates an unsigned challenge.
    • createChallenge also throws for hmacKeySignatureSecret without hmacSignatureSecret; before, it was ignored.
    • verifySolution and verifyServerSignature throw when the secret is missing or empty.
    • Framework challengeHandlers fail with a configuration error when hmacSignatureSecret is not set.
  • Algorithm names. The built-in deriveKey functions accept only these exact names and throw otherwise: SHA-256, SHA-384, SHA-512, PBKDF2/SHA-256, PBKDF2/SHA-384, PBKDF2/SHA-512, SCRYPT, ARGON2ID.

    • The Node versions used to fall back to SHA-256 hashing for lowercase and other unknown names.
  • Replay stores.

    • With a store configured, server-signed payloads must carry an id in verificationData. Sentinel always includes it.
    • A challenge whose submission failed verification is no longer marked as used and can be retried.
    • If you use a custom store, add setIfAbsent to close the concurrent-submission race. See docs/store.md, which includes a Redis example using SET … NX.
  • Browser SHA derivation on the server. If your server verifies with altcha-lib/algorithms/web/sha, for example on an edge runtime, and uses SHA-384 or SHA-512 with cost > 1 or a short keyLength, it now derives the same keys as the Node version. The widget does not do this yet (see below), so its solutions for those settings will be rejected until it is updated. The defaults (SHA-256, keyLength 32) are not affected.

  • Form fields hash. The plugins do not check fieldsHash from Sentinel. To reject submissions whose fields were changed, call verifyFieldsHash yourself; docs/server-signatures.md has an example.

See CHANGELOG.md for the full list of changes.

Don't miss a new altcha-lib release

NewReleases is sending notifications on new releases.