Phalcon v5.20.3 and v6.0.0beta11 have been released. This is a security-focused maintenance release.
TLDR
No, we did not skip a version. v5.20.2 was released on the 25th along with v6.0.0beta10. However, before we could post the article, we received emails at our security@phalcon.io address, about potential issues in several components. While those were being addressed, we also ran the framework code through Aikido, which also identified issues. The good news is that some of those had already been addressed, while others were genuine findings.
To that end, we worked to get all those issues fixed and the outcome is v5.20.3 and v6.0.0beta11.
Zephir
Additionally, Zephir has a minor version, so the framework has been compiled with 1.3.0
Note!
There are several areas that these AI tools flag as dangerous, and indeed some of them are, but there is always a balance between functionality, how much should the framework do, and what the developer is responsible for.
For example, our DataMapper component comes with a Profiler. If a developer enables the profiler and has it logging in a file, then the {values} parameter will inject all the passed parameters of every query logged into that logger. One might think that this is OK since it is the server’s local system, or even the local machine. Agreed on both; however, malicious actors do not care about that. If they find a door, they will walk right through it. So for the Profiler and other components, we ensured that there are visible callouts in the documentation to warn developers of the implications of using those components.
Thanks
We would like to extend a special thanks to Ilia Alshanetsky for his email security reports and Aikido for their service.
As always, thank you to everyone who reported an issue, opened a pull request, or tested a build before it shipped - and, this time, a particular thank you to everyone who sent a security report. Please keep looking; just send the next one to the address above.
5.20.3 (2026-08-26)
Tools
- Zephir 1.3.0
Changed
- ACL role, component and access names can no longer contain
!, the internal key delimiter;Phalcon\Acl\Exceptions\ForbiddenDelimiteris thrown instead. - Cache file names of
Phalcon\Annotations\Adapter\Stream,Phalcon\Mvc\Model\MetaData\StreamandPhalcon\Storage\Adapter\Streamget a hash suffix when the key contains the character that the separator replacement produces (_for class names;/,\,:for storage keys), so two different keys can no longer share one file. Names of all other keys are unchanged. Phalcon\Auth\Guard\Sessionsets theSecureflag of the remember-me cookie from the newrememberSecureoption (defaulttrue) instead of the request scheme, so a TLS-terminating proxy that reports plain HTTP to the backend cannot downgrade it.Phalcon\Auth\Guard\Sessionvalidates the “remember me” token against the user agent of the current request instead of the one stored in the cookie; a browser user-agent change now ends a remembered session.Phalcon\Encryption\Security::CRYPT_MD5,CRYPT_SHA256andCRYPT_SHA512are documented as weak legacy algorithms to be removed in a future major version; use bcrypt or Argon2 and rehash on login.Phalcon\Storage\Adapter\Streamcreates its shard directories with mode0755instead of0777. Thanks to Ilia Alshanetsky- The PHPUnit configuration now fails the run on notices, deprecations and PHPUnit deprecations, and prints the details of every triggering test.
- Added regression tests for the hardening changes released in 5.20.0 and 5.20.1 that had no failing-on-revert coverage.
Added
- Optional fifth argument
stopOnFalseonPhalcon\Events\Manager::fire()(not on the interface), a per-call override ofsetStopOnFalse();EventsAwareTrait::fireManagerEvent()gained a matching fourth argument. Phalcon\Acl\Exceptions\ForbiddenDelimiter, thrown when an ACL role, component or access name contains!.Phalcon\Auth\Exceptions\InvalidCredentialKey, thrown when a credential key passed toPhalcon\Auth\Adapter\Model::retrieveByCredentials()is not a plain identifier.Phalcon\Http\Request\Bag\AbstractBag::clear(), removing all elements of a request bag.allowedClassesoption for the Storage adapters (true,falseor a list of class names), forwarded to the newPhalcon\Storage\Serializer\Php::setAllowedClasses(): restricts the classesunserialize()may instantiate for stored values, including the nested content of theStreamadapter. A class outside the list makes the read fail instead of building an object. Thanks to Ilia AlshanetskyrememberSecureoption for the session guard (Phalcon\Auth\Guard\Config\SessionGuardConfig,Session::fromOptions()).
Fixed
- “Remember me” cookie of another account surviving
Phalcon\Auth\Guard\Session::logout()when the current user does not implementAuthRemember. - A
falsereturned by a listener ofacl:beforeCheckAccess,dispatch:beforeDispatch,dispatch:beforeExecuteRoute,micro:beforeHandleRouteormicro:beforeExecuteRoutebeing overwritten by a later listener that returned a non-null value; these boundaries now fire with stop-on-false, so a denial is final. - Asset output following a symbolic link at the target file and writing outside the assets directory.
- Backslash path traversal in
Phalcon\Mvc\View::partial()andPhalcon\Mvc\View\Simple::render()on Windows. - Cached user surviving
Phalcon\Auth\Guard\Token::setRequest(), so a replaced request inherited the previous authentication. - Column comment concatenated unescaped into the
CREATE TABLE/ALTER TABLEDDL of the MySQL dialect; it is now escaped like the DEFAULT clause. Thanks to Ilia Alshanetsky - Credential keys interpolated unvalidated into the PHQL built by
Phalcon\Auth\Adapter\Model::retrieveByCredentials(). - Distinct ACL tuples colliding on the same internal key when a role, component or access name contained
!. - Fixed shared memory leak of response headers into other callers
- JWT audience validated with a loose comparison, so a numeric or boolean
audclaim satisfied a string audience. - Length-dependent HMAC work on the CBC decrypt failure path of
Phalcon\Encryption\Crypt, which could still tell a padding failure from a MAC mismatch by timing. - Malformed ACL snapshot loaded by
Phalcon\Acl\Adapter\StorageraisingTypeErroror leaving the adapter half loaded, and deep or cyclic object graphs recursing without limit;InvalidSnapshotis now thrown before any state changes. - Memory leak in the PHQL parser for every rejected literal when
phalcon.orm.enable_literalsis off. - Memory leak of the error message on every failed
Phalcon\Mvc\Model\Query\Lang::parsePHQL()call (syntax and scanner errors); the string was only released at request shutdown. - Memory leak of the message buffer on every Volt syntax error (
Phalcon\Mvc\View\Engine\Volt\Compiler); the buffer was only released at request shutdown. - Namespace middleware bypass in the ADR
Routerthrough case-variant or separator-injected paths that PHP resolves to the canonical Action class; only the exact declared class name is a match. - Non-string elements passed to
Phalcon\Acl\Adapter\Memory::addInherit()raising a warning and aTypeErrorinstead ofInvalidRoleType. - Out-of-bounds read in the Annotations scanner when a docblock ends outside an annotation (for example
@!); the scanner now stops at the end of the input andReader::parseDocBlock()returnsfalseinstead of an unset value for a docblock without annotations. - Out-of-bounds read in the Annotations, Volt and PHQL scanners when a quoted string ends with a backslash: the escape rule could consume the string terminator.
- Quadratic list building in the Annotations, Volt and PHQL parsers; argument lists,
INlists and array literals with tens of thousands of items now parse in linear time. - Request attributes of the previous route surviving on a reused request in
Phalcon\ADR\Application::handle(). - Scheme allow-list bypass in the Filter
urlsanitizer through HTML-entity obfuscated schemes (javascript:) and URLs thatparse_url()cannot parse; the sanitizer now fails closed. Thanks to Ilia Alshanetsky - Validators
Alpha,Alnum,Confirmation,CreditCard,Digit,Numericality,Regex,StringLength\MinandStringLength\Maxcast an array value to the constant"Array", sofield[]=xpassed alphabetic, alphanumeric, length and confirmation checks; a value that cannot be a string is now rejected with the validator’s message. Thanks to Ilia Alshanetsky - Volt extends-mode cache unserialized without a class restriction.
ReflectionException/TypeErrorfrom ACL rule callbacks with builtin-typed parameters, array callables or static-method strings.acl:afterCheckAccessreporting the static rule instead of the finalisAllowed()decision (rule callback and default action were not applied).only()/except()action filters leaking betweenPhalcon\Auth\Manager::access()activations when the access gate was registered as a shared service in the legacyDi.Undefined indexnotice emitted for every literal route whenPhalcon\Mvc\Routerrebuilds its per-method index. #17527
Chat - Q&A
Support
Social Media
Videos
<3 Phalcon Team