{"id":"CVE-2026-54736","aliases":["GHSA-8jqh-95g6-7jpj"],"url":"https://o3.security/vulnerability/CVE-2026-54736","summary":"Phalcon: Non-constant-time HMAC verification in `Encryption\\Crypt::decrypt` (timing side-channel)","details":"## Summary\n\n`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.\n\n## Details\n\n### Vulnerable code\n\n`phalcon/Encryption/Crypt.zep:246` (Zephir source):\n\n```zephir\nif true === this->useSigning {\n    // Checks on the decrypted message digest using the HMAC method.\n    if digest !== hash_hmac(hashAlgorithm, padded, decryptKey, true) {\n        throw new Mismatch(\"Hash does not match.\");\n    }\n}\n```\n\nGenerated C --> `ext/phalcon/encryption/crypt.zep.c:364-367`:\n\n```c\nZEPHIR_CALL_FUNCTION(&_8$$7, \"hash_hmac\", NULL, 245, &hashAlgorithm, &padded, &decryptKey, &__$true);\n...\nif (!ZEPHIR_IS_IDENTICAL(&digest, &_8$$7)) {                 // <-- non-constant-time\n    ZEPHIR_THROW_EXCEPTION_DEBUG_STR(..., \"Hash does not match.\", \"phalcon/Encryption/Crypt.zep\", 247);\n```\n\n`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).\n\n\n\n### Impact\n\nThe 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.\n\n## Suggested fix\n\nReplace the identity comparison with the constant-time helper already used elsewhere in the framework. In `phalcon/Encryption/Crypt.zep:246`:\n\n```zephir\n// before\nif digest !== hash_hmac(hashAlgorithm, padded, decryptKey, true) {\n    throw new Mismatch(\"Hash does not match.\");\n}\n// after\nif true !== hash_equals(hash_hmac(hashAlgorithm, padded, decryptKey, true), digest) {\n    throw new Mismatch(\"Hash does not match.\");\n}\n```\n\n`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`.\n\nAddressed Issue: \n\n- https://github.com/phalcon/cphalcon/issues/17090\n\nPatched Stream: \n\n- https://github.com/phalcon/cphalcon/issues/17090","published":"2026-07-10T21:02:53.873Z","modified":"2026-08-12T03:51:13.817311910Z","cvss":null,"epss":{"score":0.0018,"percentile":0.07866,"asOf":"2026-09-16"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Packagist","name":"phalcon/cphalcon","fixedVersion":"5.14.1"}],"fix":{"url":"https://github.com/phalcon/cphalcon/commit/ad53ab1b2e7ec59b3af92b0b37b8aaa099011137","label":"phalcon/cphalcon@ad53ab1"},"references":[{"type":"WEB","url":"https://github.com/phalcon/cphalcon/releases/tag/v5.14.1"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54736.json"},{"type":"ADVISORY","url":"https://github.com/phalcon/cphalcon/security/advisories/GHSA-8jqh-95g6-7jpj"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-54736"},{"type":"REPORT","url":"https://github.com/phalcon/cphalcon/issues/17090"},{"type":"FIX","url":"https://github.com/phalcon/cphalcon/commit/ad53ab1b2e7ec59b3af92b0b37b8aaa099011137"},{"type":"FIX","url":"https://github.com/phalcon/cphalcon/pull/17091"},{"type":"PACKAGE","url":"https://github.com/phalcon/cphalcon"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:13.817311910Z"}}