GIVE YOUR AGENT THE EVIDENCE

Set up in your agent.

One prompt to get started. Paste it into your coding agent to connect cve.new through MCP and bring vulnerability context into your workflow.

Your agent needs remote MCP support and access to https://cve.new/mcp.

Read the setup guide

GIVE YOUR AGENT THE EVIDENCE

Investigate in your agent.

Turn this advisory into a focused investigation. Ask your agent to check your project’s versions, explain the evidence, and suggest a compatible fix.

Your agent needs remote MCP support and access to https://cve.new/mcp.

Read the setup guide

GHSA-8jqh-95g6-7jpjhigh8.2

Phalcon: Non-constant-time HMAC verification in `Encryption\Crypt::decrypt` (timing side-channel)

Check your stack

The advisory, in context

Summary

Phalcon\Encryption\Crypt provides authenticated encryption: when useSigning is enabled (the default), encrypt() appends an HMAC tag and decrypt() verifies it before returning the plaintext. The verification compares the attacker-supplied tag against the freshly computed HMAC using PHP/Zephir identity comparison (!==), which the Zephir compiler lowers to !ZEPHIR_IS_IDENTICAL(...) — a byte-wise memcmp that returns early on the first differing byte. The comparison time therefore depends on how many leading bytes of the supplied tag are correct, a classic MAC-verification timing side-channel. Every other secret/MAC comparison in the framework uses the constant-time hash_equals() (zephir_hash_equals) — the CSRF token check (Security::checkToken) and the JWT signature check (Signer\Hmac::verify); Crypt::decrypt is the lone deviation.

Details

Vulnerable code

phalcon/Encryption/Crypt.zep:246 (Zephir source):

if true === this->useSigning {
    // Checks on the decrypted message digest using the HMAC method.
    if digest !== hash_hmac(hashAlgorithm, padded, decryptKey, true) {
        throw new Mismatch("Hash does not match.");
    }
}

Generated C --> ext/phalcon/encryption/crypt.zep.c:364-367:

ZEPHIR_CALL_FUNCTION(&_8$$7, "hash_hmac", NULL, 245, &hashAlgorithm, &padded, &decryptKey, &__$true);
...
if (!ZEPHIR_IS_IDENTICAL(&digest, &_8$$7)) {                 // <-- non-constant-time
    ZEPHIR_THROW_EXCEPTION_DEBUG_STR(..., "Hash does not match.", "phalcon/Encryption/Crypt.zep", 247);

ZEPHIR_IS_IDENTICAL --> zephir_is_identical() (ext/kernel/operators.c:472) --> Zend is_identical_function --> for equal-length strings a memcmp that exits on the first mismatching byte (data-dependent timing).

Impact

The HMAC is the integrity/authentication tag of Phalcon's authenticated-encryption scheme. A successful timing attack (Keyczar/CVE-2009-0654-style: fix the IV+ciphertext so the target tag is constant, then recover it byte-by-byte from response timing) yields a tag the attacker can attach to a chosen IV+ciphertext so that decrypt() accepts it as authentic, defeating the integrity guarantee. Combined with CFB malleability (flipping a ciphertext byte flips the corresponding plaintext byte), an attacker who recovers the forging capability can tamper with the decrypted contents the application trusts (e.g. encrypted cookies carrying authorization/identity state). There is no confidentiality break by itself.

Suggested fix

Replace the identity comparison with the constant-time helper already used elsewhere in the framework. In phalcon/Encryption/Crypt.zep:246:

// before
if digest !== hash_hmac(hashAlgorithm, padded, decryptKey, true) {
    throw new Mismatch("Hash does not match.");
}
// after
if true !== hash_equals(hash_hmac(hashAlgorithm, padded, decryptKey, true), digest) {
    throw new Mismatch("Hash does not match.");
}

hash_equals() returns false for unequal-length inputs, so it also covers the truncated-tag case. Optional further hardening: verify the MAC before unpadding (functionally moot here because cryptUnpadText never throws) and consider migrating the default toward an AEAD mode such as aes-256-gcm.

Addressed Issue:

Patched Stream:

Affected packages

Start with the recorded fix for your release branch. Check your installed version and test compatibility before upgrading. Fixing this advisory does not rule out other vulnerabilities in the release.

Ecosystem versions

Earlier releases before 5.14.1

Fixed in 5.14.1

94 explicitly affected versions

5.10.0, 5.12.0, 5.9.0, 5.9.1, 5.9.2, 5.9.3, v3.0.0, v3.0.1, v3.0.2, v3.0.3, v3.0.4, v3.1.0, v3.1.0-alpha1, v3.1.0-alpha2, v3.1.1, v3.1.2, v3.2.0, v3.2.1, v3.2.2, v3.2.3, v3.2.4, v3.3.0, v3.3.1, v3.3.2, v3.4.0, v3.4.1, v3.4.2, v3.4.3, v3.4.4, v3.4.5, v4.0.0, v4.0.0-alpha.2, v4.0.0-alpha.3, v4.0.0-alpha.4, v4.0.0-alpha.5, v4.0.0-alpha1, v4.0.0-beta.1, v4.0.0-beta.2, v4.0.0-rc.1, v4.0.0-rc.2, v4.0.0-rc.3, v4.0.1, v4.0.2, v4.0.3, v4.0.4, v4.0.5, v4.0.6, v4.1.0, v4.1.1, v4.1.2, v4.1.3, v5.0.0, v5.0.0-alpha.1, v5.0.0-alpha.2, v5.0.0RC1, v5.0.0RC2, v5.0.0RC3, v5.0.0RC4, v5.0.0alpha3, v5.0.0alpha4, v5.0.0alpha5, v5.0.0alpha6, v5.0.0alpha7, v5.0.0beta1, v5.0.0beta2, v5.0.0beta3, v5.0.1, v5.0.2, v5.0.3, v5.0.4, v5.0.5, v5.1.0, v5.1.1, v5.1.2, v5.1.3, v5.1.4, v5.11.0, v5.11.1, v5.12.1, v5.13.0, v5.14.0, v5.2.0, v5.2.1, v5.2.2, v5.2.3, v5.3.0, v5.3.1, v5.4.0, v5.5.0, v5.6.0, v5.6.1, v5.6.2, v5.7.0, v5.8.0

View affected-version evidence
{
  "ranges": [
    {
      "events": [
        {
          "introduced": "0"
        },
        {
          "fixed": "5.14.1"
        }
      ],
      "type": "ECOSYSTEM"
    }
  ],
  "versions": [
    "5.10.0",
    "5.12.0",
    "5.9.0",
    "5.9.1",
    "5.9.2",
    "5.9.3",
    "v3.0.0",
    "v3.0.1",
    "v3.0.2",
    "v3.0.3",
    "v3.0.4",
    "v3.1.0",
    "v3.1.0-alpha1",
    "v3.1.0-alpha2",
    "v3.1.1",
    "v3.1.2",
    "v3.2.0",
    "v3.2.1",
    "v3.2.2",
    "v3.2.3",
    "v3.2.4",
    "v3.3.0",
    "v3.3.1",
    "v3.3.2",
    "v3.4.0",
    "v3.4.1",
    "v3.4.2",
    "v3.4.3",
    "v3.4.4",
    "v3.4.5",
    "v4.0.0",
    "v4.0.0-alpha.2",
    "v4.0.0-alpha.3",
    "v4.0.0-alpha.4",
    "v4.0.0-alpha.5",
    "v4.0.0-alpha1",
    "v4.0.0-beta.1",
    "v4.0.0-beta.2",
    "v4.0.0-rc.1",
    "v4.0.0-rc.2",
    "v4.0.0-rc.3",
    "v4.0.1",
    "v4.0.2",
    "v4.0.3",
    "v4.0.4",
    "v4.0.5",
    "v4.0.6",
    "v4.1.0",
    "v4.1.1",
    "v4.1.2",
    "v4.1.3",
    "v5.0.0",
    "v5.0.0-alpha.1",
    "v5.0.0-alpha.2",
    "v5.0.0RC1",
    "v5.0.0RC2",
    "v5.0.0RC3",
    "v5.0.0RC4",
    "v5.0.0alpha3",
    "v5.0.0alpha4",
    "v5.0.0alpha5",
    "v5.0.0alpha6",
    "v5.0.0alpha7",
    "v5.0.0beta1",
    "v5.0.0beta2",
    "v5.0.0beta3",
    "v5.0.1",
    "v5.0.2",
    "v5.0.3",
    "v5.0.4",
    "v5.0.5",
    "v5.1.0",
    "v5.1.1",
    "v5.1.2",
    "v5.1.3",
    "v5.1.4",
    "v5.11.0",
    "v5.11.1",
    "v5.12.1",
    "v5.13.0",
    "v5.14.0",
    "v5.2.0",
    "v5.2.1",
    "v5.2.2",
    "v5.2.3",
    "v5.3.0",
    "v5.3.1",
    "v5.4.0",
    "v5.5.0",
    "v5.6.0",
    "v5.6.1",
    "v5.6.2",
    "v5.7.0",
    "v5.8.0"
  ]
}

References