Problem/Motivation
The release of symfony/http-foundation v4.4.42 validates and recreates the session ID when it's not valid.
\Drupal\Core\Session\SessionManager::getId() generates a random session ID if a session is not started yet.
The ID generated through \Drupal\Component\Utility\Crypt::randomBytesBase64() can contain invalid characters such as underscores.
The next time that the session is started, the Symfony component will regenerate the id and the session data will be lost.
Calling \Drupal\Core\Session\SessionManager::getId() is already deprecated as indicated in code and in this change record.
Steps to reproduce
This issue was encountered during tests while using the cas module version 1.7.0 but it can be reproduced by calling \Drupal\Core\Session\SessionManager::getId() before a session exists.
Proposed resolution
None at the moment, I'm creating this just as a reference for users that might run into the same.
It can be fixed by
- not calling getId() and using alternatives as explained in change record Drupal uses PHP session ID generation.
- locking symfony/http-foundation to 4.4.41, at least until the point above is fixed.
Writing a test is hard as the ID generation is random so it fails only sometimes.
Remaining tasks
To be discussed, since now calling the above method can actually create issues.
User interface changes
None.
API changes
TBD.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | 3285696-9.5.x-20.patch | 3.46 KB | alexpott |
| #20 | 3285696-9.4.x-20.patch | 3.46 KB | alexpott |
| #11 | 3285696-11.patch | 5.23 KB | longwave |
| #9 | 3285696-9-downgrade-http-foundation.patch | 7.74 KB | longwave |
Issue fork drupal-3285696
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
sardara commentedComment #3
alexpottThis could be a critical regression in 9.4.0 which is due out tomorrow.
Comment #4
alexpottI would imagine this might affect the flag module as well. Lazy sessions and having an ID before has been complex and source of bugs over the years.
Comment #6
sardara commentedPushed a test. Since the session ID generation is not always invalid, the test attempts up to 10 times to fail.
Comment #7
sardara commentedComment #8
longwaveConfirmed locally. With the MR applied and symfony/http-foundation:v4.4.42:
This is repeatable and fails somewhere in the first 3 iterations.
Fixed by downgrading:
Comment #9
longwaveAttached patch forces a downgrade of
symfony/http-foundationuntil we figure out a better solution.Comment #10
longwaveMaybe the fix should be that
SessionManager::getId()repeatedly callsCrypt::randomBytesBase64()until it gets a result that matches the same regex Symfony uses?Or even, we just replace
_with,as Symfony allows that in/^[a-zA-Z0-9,-]{22,}$/?Comment #11
longwaveImplementation of #10 plus fix for the use of deprecated code in the test.
Comment #12
sardara commentedI pushed a new test which can fail consistently. I didn't remove the old test yet.
I am not also very happy with the "legacy" word I'm using around, maybe "deprecated" is a better fit.
@longwave thanks for testing locally.
Yes the replace is the fix I was preparing too, I just pushed something similar.
Comment #13
sardara commentedI cannot think of a way to write a test that doesn't rely on multiple retries to test that only "correct" ids are generated.
Unfortunately
Crypt::randomBytesBase64()cannot be influenced in any way to return a string with an underscore.Comment #14
alexpottI think the upstream change is wrong and should be reverted. The list of characters they are using is based on file based session handling and the characters that are legal for a cookie are different. See https://github.com/symfony/symfony/pull/46249#issuecomment-1155769309
Comment #16
quietone commentedFix link in the IS
Comment #17
sardara commentedGiven the random id generation will be gone in 10.0.0, I believe the approach of #11 is good.
Maybe tests and comments I wrote could be polished, but the fix by @longwave is correct, underscore is the only invalid character returned by
Crypt::randomBytesBase64().That is if we need to merge something to unblock 9.4.0. I agree with @alexpott for upstream. Along with an arbitrary character list, it looks the wrong place and time to regenerate an ID, as the session data loss proves. Also the public ::setId() method has no validation whatsoever so why ::start() should change ID.
Comment #18
alexpottFWIW the session ID will be url encoded automagically by PHP - see documentation for setcookie - https://www.php.net/manual/en/function.setcookie.php
I agree that #11 is a good fix for now to prevent regressions for 9.4.0 but also ensure that if there is a security release for http-foundation it means we can move to it without having to think too much.
The fact that the test added here is not guaranteed to fail without the fix is very very icky. So I think we could consider committing #11 without the tests. OTOH the tests failed all the times on this issue without the fix so maybe leaving them is okay. Will leave up to a release manager to decide - I could go either way.
Comment #19
alexpottI think Symfony might revert the change - see https://github.com/symfony/symfony/pull/46678 - so maybe the best thing to do here will to add a conflict on 4.4.42 and then we can get 4.4.43 when it comes out. The session ID validity check is the only change from 4.4.41 to 4.4.42 - see https://github.com/symfony/http-foundation/compare/v4.4.41...v4.4.42
Going to create a patch based on that because this is certainly the least risky approach.
Comment #20
alexpottHere's the patches that declare a conflict. If symfony decides to revert then we should use them instead of #11.
Comment #21
catchDeclaring a conflict seems good - given this is time-sensitive we might need to guess what they'll do (i.e. commit the conflict, open a follow-up to track whether they revert or not).
If we go with using commas instead of underscores, given the method is already deprecated and hard to test, and it's a one-line fix that we're very unlikely to ever touch until we remove the whole method, would be fine with committing the fix itself without test coverage.
Comment #22
alexpottThe more I think about it the more I think that doing #20 for now is the best option for today. The change in 4.4.42 will impact sites using contrib modules that depend on this. People with existing sessions that have an underscore in them will lose their sessions in the upgrade if we release with 4.4.42 - and that would not normally occur during an upgrade.
Comment #26
catchOK I've committed the 9.5.x and 9.4.x patches.
The same change is in Symfony 6, but we don't care in Drupal 10 because the deprecated method no-longer exists there.
Opened #3285817: Symfony 4.4.42 breaks legacy random session ID generation, adapt it if there's no rollback to track whether upstream reverts or not and if we still need to implement the fix here or not.
Comment #28
avpadernoI replaced the used issue tag with the official one. I apologize for bumping this issue.