CRZ-012

Corezoid public API uses SHA-1 (non-HMAC) for request signing

medium CVSS 3.1: 3.9 · Asset: api.corezoid.com + all /api/2/*

Summary

The Corezoid public API signs requests using SHA-1 (not HMAC-SHA256+), with a manual keyed-hash construction rather than a standard HMAC:

// from openapi.corezoid.com documentation
signature = CryptoJS.enc.Hex.stringify(
  CryptoJS.SHA1(time + secret + content + secret)
);

This construction has two distinct issues:

Issue A — SHA-1 is cryptographically deprecated

Issue B — Custom SHA(k‖m‖k) is not HMAC

HMAC is defined in RFC 2104 with a specific inner/outer padding construction that protects against length-extension attacks. The Corezoid pattern SHA1(time + secret + content + secret) is the "key-then-message-then-key" envelope-MAC, which provides some protection over the naive SHA1(key + message) but is:

Reproduction — confirm the signing scheme

$ curl -sk 'https://openapi.corezoid.com/' | grep -oE 'signature\s*=\s*CryptoJS[^<]{0,200}'
signature = CryptoJS.enc.Hex.stringify(CryptoJS.SHA1(time + secret + content + secret));

Also visible in Postman collection examples and the docs/corezoid-api.json spec (70 paths, all using {SIGNATURE} path parameter).

Impact

Direct exploitation is difficult today — SHA-1 collisions cost money and time, and targeted collisions on time + secret + content + secret are harder still because the attacker doesn't know secret. The realistic attack scenarios are:

  1. Compliance failure. PCI DSS 4.0 (effective 2025), HIPAA, EU NIS2 for critical infrastructure, and many banking regulators explicitly require SHA-2 family hashes for new implementations. For any Corezoid customer in a regulated industry, SHA-1 signing is an audit blocker.
  2. Collision attack becomes cheaper over time. In 2017, a full SHA-1 collision cost ~$110K of GPU time. By 2020 it was 45K(chosen − prefix).By2026withconsumerGPUsandcloudcompute, chosen − prefixSHA − 1collisionsareplausibly<10K — still not trivial for internet-random attackers, but affordable for motivated adversaries.
  3. Key-material leakage amplifies risk. If the API secret for a single customer leaks (via log exposure, HAR capture, CI dump, etc.), an attacker who captured historical request/response pairs can verify their stolen secret works — and SHA-1's structural weaknesses give them additional signature-forgery options that HMAC-SHA256 would close off.
  4. Downgrade scenario. If Corezoid ever ships a client library that accepts "SHA1 OR SHA256 signatures" during a migration, attackers can force-downgrade by omitting the algorithm-identifier header.

Remediation

Priority 1 — add SHA-256 as primary, keep SHA-1 for backward compat:

  1. Introduce HMAC-SHA256 as the preferred signature:
    signature = HMAC-SHA256(key = secret, message = time + content).hex()
  2. Accept both SHA-1 and HMAC-SHA256 signatures during migration, but add a header (e.g., X-Signature-Version: 2) so the server knows which is being used. Without an explicit version header, reject the request.
  3. Warn customers via API response header: Deprecation: version="1.0", sunset=2027-01-01 when a SHA-1 signature is used.
  4. Publish a migration guide.

Priority 2 — enforce HMAC-SHA256 only:

  1. After 6-12 months of dual-acceptance + deprecation warnings, disable SHA-1 signatures entirely.
  2. Update client SDKs, Postman collections, openapi.corezoid.com docs, corezoid-ai-doc repo.

Priority 3 — strengthen the construction:

  1. Use standard HMAC (RFC 2104), not envelope-MAC. HMAC-SHA256 output should be the hex representation of the full 32-byte digest.
  2. Include the full HTTP method, URL path, query string, and request body in the signed content — otherwise an attacker who captures a valid signature can replay it against different endpoints (partial-URL replay attack).
  3. Tighten the GMT_UNIXTIME freshness window to ±60 seconds (common: ±5 minutes is common but excessive).
  4. Include a nonce in the signed content to prevent replay within the freshness window.

Context — what's working well

References