GHSA-8jqh-95g6-7jpj: CSRF

Published Aug 28, 2026
·
Updated

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 !ZEPHIRISIDENTICAL(...) — 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 hashequals() (zephirhashequals) — 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):

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

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

c ZEPHIRCALLFUNCTION(&8$$7, "hashhmac", NULL, 245, &hashAlgorithm, &padded, &decryptKey, &$true); ... if (!ZEPHIRISIDENTICAL(&digest, &8$$7)) { // <-- non-constant-time ZEPHIRTHROWEXCEPTIONDEBUGSTR(..., "Hash does not match.", "phalcon/Encryption/Crypt.zep", 247);

ZEPHIRISIDENTICAL --> zephirisidentical() (ext/kernel/operators.c:472) --> Zend isidenticalfunction --> 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:

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

hashequals() 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:

- https://github.com/phalcon/cphalcon/issues/17090

Patched Stream:

- https://github.com/phalcon/cphalcon/issues/17090

Affected Software

1 affected componentFixes available
composer/phalcon/cphalcon<=5.14.0
5.14.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/phalcon/cphalcon to a version that resolves this vulnerability.

    Fixed in 5.14.1
  2. Configuration

    In `phalcon/Encryption/Crypt.zep:246`, replace the non-constant-time identity comparison (`ZEPHIR_IS_IDENTICAL(&digest, ...)` / `digest !== ...`) used for verifying the HMAC tag with the constant-time helper `hash_equals(hash_hmac(...), digest)` as shown in the suggested fix snippet (`if true !== hash_equals(..., digest) { throw new Mismatch("Hash does not match."); }`).

    Phalcon\Encryption\Crypt (Crypt.zep) MAC verification comparison function = hash_equals

Event History

Aug 28, 2026
Advisory Published
via GitHub·04:01 PM
Data Sourced
via GitHub·04:01 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Are applications using the default Crypt configuration affected?

Yes. useSigning is enabled by default, so Crypt::decrypt performs the non-constant-time HMAC comparison unless that setting has been changed.

2

What capability would an attacker need to exploit the issue?

The attacker needs to supply authentication tags to Crypt::decrypt and measure how long verification takes. Tags with more correct leading bytes take longer to compare, allowing timing observations to reveal information about the HMAC.

3

Which verification path is affected?

The affected path is the HMAC tag check performed by Phalcon\Encryption\Crypt::decrypt. The CSRF token and JWT HMAC checks are described as using hash_equals() instead.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203