No Code Attached Yet
avatar NicolasDerumigny
NicolasDerumigny
19 Dec 2025

Problem identified

Some browsers have the feature of restoring all tabs on launch. If numerous tabs are opened, this sends multiple requests to Joomla! in parallel (even more if keepalive pings are "remembered" and issued at the same time than tab requests, which I have seen in the wild) with the same value of the authentication cookie. This is identified as an attack, resulting in user sessions being purged.

The issue comes from plugins/authentication/cookie/src/Extension/Cookie.php, whose role is:

  • hooked to onUserAuthenticate: check that the cookie is correct w.r.t. user_keys content
  • hooked to onUserAfterLogin: update the cookie and the user_keys entry, or create both on first login when "remember me" option is set

When several request arrives at the same time, then:

  • some of them pass onUserAuthenticate, then land in onUserAfterLogin which modifies (several times) the cookie and the db. I assume this is in all cases unwanted behavior and a vulnerability.
  • others fail at onUserAuthenticate because the db was updated
  • the latter are considered to be a cookie replay attack so any session of the user in user_keys is destroyed

Proposed solution

Use transaction on the db with a lock at the row granularity to serialize access to the corresponding user_keys entry. This means that the cookie update is moved to onUserAuthenticate (and creation stays in onUserAfterLogin), so that the transaction starts and commit / abort in the same function scope. The corresponding requests are:

START TRANSACTION;
SELECT .... from user_keys WHERE cookie_is_valid FOR UPDATE; # blocking read, serialization point
UPDATE user_keys SET .... WHERE cookie_is_valid;
COMMIT;

This will guarantee that one cookie gets updated only once, and all subsequent requests will fail, avoiding both the security issue mentioned earlier and the nondeterministic way the user's cookie is updated when several updates to the db are done in parallel.

When the transaction fails because the cookie is not valid, it is either an attack or multiple request. My guess would be to throw an error message similar to "too many autologin tentatives, please try again" and not delete all the sessions neither the cookie, as it may be updated (or have been updated) by another request / tab.

PoC code is available in this patch. Note that there is a subtlety: cookie login is still attempted if the cookie is present but invalid and a user logins, case in which the message should not be displayed (which we can detect by the presence of input->getVar('username').

Open questions

  • What is the security impact of not deleting all sessions? If an attacker had access to a cookie, it is useless to remove it for the browser ; but what about db entries? Should we regenerate a series?
  • FOR UPDATE is currently not wrapped on Joomla! 6 mysqli back-end. This would need to be done (or hacked) for this fix to be integrated. Do we want that?
  • Transaction will incur performance degradation, but a lock at the row level should only be noticeable when the same user tries to login simultaneously. Is this acceptable?
avatar NicolasDerumigny NicolasDerumigny - open - 19 Dec 2025
avatar joomla-cms-bot joomla-cms-bot - change - 19 Dec 2025
Labels Added: No Code Attached Yet
avatar joomla-cms-bot joomla-cms-bot - labeled - 19 Dec 2025
avatar NicolasDerumigny NicolasDerumigny - change - 19 Dec 2025
The description was changed
avatar NicolasDerumigny NicolasDerumigny - edited - 19 Dec 2025
avatar NicolasDerumigny NicolasDerumigny - change - 19 Dec 2025
The description was changed
avatar NicolasDerumigny NicolasDerumigny - edited - 19 Dec 2025

Add a Comment

Login with GitHub to post a comment