Unit/System Tests PR-5.4-dev Pending

User tests: Successful: Unsuccessful:

avatar ManhThuan
ManhThuan
28 May 2026

Adds the allowed_classes option to unserialize() in JoomlaStorage::start() so that only Joomla\Registry\Registry objects can be reconstituted from session data. A malicious or corrupted session value can no longer trigger PHP Object Injection; non-Registry payloads are silently discarded and the session falls back to an empty Registry, requiring the user to log in again.

Pull Request resolves # .

  • I read the Generative AI policy and my contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later.

Summary of Changes

Restricts unserialize() in JoomlaStorage::start() to only allow Joomla\Registry\Registry via the allowed_classes option, then guards the assignment with an instanceof Registry check. Any serialised payload that is not a Registry is silently discarded and the session falls back to the empty Registry initialised in the constructor.

The write path (close()) always stores a serialised Registry, so legitimate sessions are completely unaffected. Sessions are transient, so there is no long-term data loss.

Testing Instructions

Run the new unit tests:

vendor/bin/phpunit tests/Unit/Libraries/Cms/Session/Storage/JoomlaStorageTest.php

Four test cases verify:

  1. A valid Registry payload loads correctly.
  2. A serialised stdClass is rejected and not returned as stdClass.
  3. A non-Registry object causes a fallback to an empty Registry.
  4. Corrupted base64 data causes a fallback to an empty Registry.

Actual result BEFORE applying this Pull Request

unserialize() in JoomlaStorage::start() has no class restriction. Any PHP class available in the autoloader can be instantiated from a crafted $_SESSION['joomla'] value, enabling a PHP Object Injection attack (OWASP A08:2021).

Expected result AFTER applying this Pull Request

Only Joomla\Registry\Registry objects can be deserialised from session data. Any other payload is discarded and the session silently falls back to an empty Registry.

Link to documentations

  • No documentation changes for guide.joomla.org needed
  • No documentation changes for manual.joomla.org needed
avatar ManhThuan ManhThuan - open - 28 May 2026
avatar ManhThuan ManhThuan - change - 28 May 2026
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 28 May 2026
Category Libraries Unit Tests
avatar ManhThuan ManhThuan - change - 28 May 2026
Labels Added: Unit/System Tests PR-5.4-dev
avatar wilsonge
wilsonge - comment - 28 May 2026
avatar SniperSister
SniperSister - comment - 28 May 2026

Thanks! In general, I think that the risk for such a scenario is near zero - however it's perfectly fine to add it as a defence-in-depth hardening

avatar joomdonation
joomdonation - comment - 28 May 2026

Wouldn't this prevent store object of custom classes in session, mean potential backward incompatible change?

avatar SniperSister
SniperSister - comment - 28 May 2026

You mean when a dev does not store primitive values but complex objects in the session? Yes, indeed that would break.

avatar joomdonation
joomdonation - comment - 28 May 2026

You mean when a dev does not store primitive values but complex objects in the session? Yes, indeed that would break.

Yes. I think we should not accept this change because as you said: the risk for such a scenario is near zero and this could be a potential break for third party extensions.

avatar ManhThuan
ManhThuan - comment - 28 May 2026

That is a valid concern. Any extension that stores instances of custom
classes directly in the Joomla session (rather than primitive values or
arrays) would be affected, as those classes are not included in the
allowed_classes whitelist.

Would it be preferable to:

  1. Accept this as a documented breaking change for 5.4 (with a note in
    the upgrade guide), or
  2. Drop the allowed_classes restriction and keep only the instanceof
    guard + RuntimeException, which still hardens against a corrupted
    session without touching B/C?
avatar ManhThuan
ManhThuan - comment - 28 May 2026

That is a valid concern. Any extension that stores instances of custom
classes directly in the Joomla session (rather than primitive values or
arrays) would be affected, as those classes are not included in the
allowed_classes whitelist.

Would it be preferable to:

  1. Accept this as a documented breaking change for 5.4 (with a note in
    the upgrade guide), or
  2. Drop the allowed_classes restriction and keep only the instanceof guard + RuntimeException, which still hardens against a corrupted
    session without touching B/C?
avatar ManhThuan
ManhThuan - comment - 28 May 2026

That is a valid concern. Any extension that stores instances of custom classes directly in the Joomla session (rather than primitive values or arrays) would be affected, as those classes are not included in the allowed_classes whitelist.

Would it be preferable to:

  1. Accept this as a documented breaking change for 5.4 (with a note in the upgrade guide), or
  2. Drop the allowed_classes restriction and keep only the instanceof guard + RuntimeException, which still hardens against a corrupted session without touching B/C?
avatar laoneo
laoneo - comment - 28 May 2026

When the data can't be loaded we should not throw an exception without resetting the session data. The site will never load again when invalid data is stored. The visitor needs to clear the session cookie then in the browser dev tools. Not sure if we want that. Current behavior is that only the session data is lost which is enough in my opinion.

avatar ManhThuan
ManhThuan - comment - 28 May 2026

When the data can't be loaded we should not throw an exception without resetting the session data. The site will never load again when invalid data is stored. The visitor needs to clear the session cookie then in the browser dev tools. Not sure if we want that. Current behavior is that only the session data is lost which is enough in my opinion.

Valid point — throwing without resetting would leave the user in an unrecoverable loop.

Would it be acceptable to keep the allowed_classes restriction (for PHP Object Injection defense) but revert to the silent fallback: discard the invalid session data, reset to an empty Registry, and let the request continue? The user gets logged out rather than locked out, which is the current behaviour and is the right tradeoff.

The B/C concern about custom class objects in session would still apply to the allowed_classes restriction — happy to drop that too if the team prefers the instanceof guard alone.

avatar laoneo
laoneo - comment - 28 May 2026

Honestly I would leave it as is and close the pr.

avatar joomdonation
joomdonation - comment - 28 May 2026

Current behavior is that only the session data is lost which is enough in my opinion

It's not really true. When session data could not be unserialized for some reasons, we will soon get fatal error when there is code uses session API (because $this->data in the class is not a Registry object anymore)

I think the check + log like below code would be better:

 if (!empty($_SESSION['joomla'])) {
	$data = unserialize(base64_decode($_SESSION['joomla']));

	if ($data instanceof Registry) {
		$this->data = $data;
	} else {
		// Log error s
	}
}
avatar wilsonge
wilsonge - comment - 28 May 2026

Wouldn't this prevent store object of custom classes in session, mean potential backward incompatible change?

How would sessions actually store the data to a custom format? We would still accept the data in whatever format in the Registry object and with the close method isn't is always just a registry object? Like I'm not clear practically how a session could override during the closing of the session without fully replacing Joomla's session handler (and in that case they are overriding the start method too?) I'd say this seems like a reasonable sanity check.

avatar joomdonation
joomdonation - comment - 29 May 2026

@wilsonge I guess you mis-understood my comment. Yes, the whole session data is stored in a registry object. The problem is that we currently allow storing data in session not only for primitive data type such as int, string, array... but also objects from any PHP class.

The modification in this PR will only allows unserialize objects from Registry::class, \stdClass::class, so if someone store object of custom classes different from the two allowed, it could not be unserialized and will make the extension brokens. Even our core code will also broken because we store object of User class in session and that could not be unserialized with the changes in this PR.

Add a Comment

Login with GitHub to post a comment