Problem/Motivation
In \Drupal\Core\Access\CsrfTokenGenerator::validate() it's possible that $token
is something other than a string. In PHP 8 this causes a warning.
The only times $token is something other than a string is in our test coverage - \Drupal\Tests\Core\Access\CsrfTokenGeneratorTest. On PHP < 8 this method will return FALSE in these case because that's the behaviour of hash_equals(). In PHP 8 hash_equals only accepts strings therefore we need to check the type of $token and return early. See https://3v4l.org/KFNqm
Not that once we change \Drupal\Core\Access\CsrfTokenGenerator::validate() to use scalar typehints this "problem" goes away.
Proposed resolution
Ensure that $token is a string.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Comment | File | Size | Author |
---|---|---|---|
#2 | 3156880-2.patch | 748 bytes | alexpott |
Comments
Comment #2
alexpottComment #3
alexpottComment #4
alexpottSo looking at https://3v4l.org/KFNqm I really wonder how useful this change is. If you're passing something that's not string on a real site you are already triggering warnings. In fact in order to maintain BC we need to trigger a warning from...
... when we return false. The only reason the test passes is because we do some jiggery-pokery with the error handler:
Comment #5
andypostIt may use deprecation to pass not string and strict type in d10, also check could be used to skip compute
Comment #6
Gábor Hojtsy@alexpott: based on your link what you mean is PHP 5 and 7 already have a Warning if what is passed is not a string, so that would surface somehow for people currently. Copying here for posterity:
Output for 7.3.0 - 7.3.21, 7.4.0 - 7.4.10
Output for 5.6.0 - 5.6.40, 7.0.0 - 7.0.33, 7.1.0 - 7.1.33, 7.2.0 - 7.2.33
Do we consider it part of the (Drupal) API that a warning is thrown? We could only throw a userspace warning anyway, so we could not reproduce the same system warning per say I think. So not sure it is worth reproducing that.
I would either remove the test from CsrfTokenGeneratorTest as the situation is already producing warnings on the site. Or commit this patch as-is if for some reason we think non-strings should be supported. Let me dig into the history of CsrfTokenGeneratorTest.
Comment #7
Gábor HojtsyThe test coverage was added in #2141041: CsrfTokenGenerator::validate() should do an identical compare then refactored in #2145881: Remove string casting from Crypt::hmacBase64(). Throw exceptions instead to throw exceptions instead of type casting for non-scalars. It does not type-cast scalars anymore to strings though, as the later issue claims they could be non-strings.
I did not find proof that non-strings would be accepted by either https://www.php.net/manual/en/function.base64-encode.php or https://www.php.net/manual/en/function.hash-hmac.php though so I don't think that was even true then.
Comment #8
Gábor HojtsyBased on my above research, all in all I think the safest is to keep the code not throwing Fatal errors on PHP 8 for things we thought we support but PHP clearly did not support. Which is what the patch does.
Comment #9
alexpott@andypost asked
The reason we do this after the call to $this->computeToken() is to maintain BC. If we do
first then \Drupal\Tests\Core\Access\CsrfTokenGeneratorTest::testInvalidParameterTypes() fails in 3 out of the four cases.
Comment #10
hussainwebI'm wondering if it's better to throw an exception in
\Drupal\Core\Access\CsrfTokenGenerator::validate
to match the behaviour, rather than maintain this order in code (which might not be intuitive later). I'm guessing the exceptions are being thrown already by\Drupal\Component\Utility\Crypt::hmacBase64
anyway.OR does this sound like a job for a follow-up?
Comment #11
alexpott@hussainweb I don't think we should throw an exception in this issue because that is a behaviour change. We should be fixing this in a way that can go all the way back to 8.9.x with the minimum of impact.
Comment #12
hussainweb@alexpott, yeah, scratch that. I am mixing up $token and $value in the code. We can change the test to make sure that the code is logical and easy to read but that would imply change of behaviour and it's difficult to imagine that. It's best to not touch that in this issue.
Comment #13
andypostI'm also ++ to improve code instead of just workaround, so why not just change tests and file separate issue for 8.9.x to keep the same unit tests.
I find strange that #9 said about calling of protected method means API/behavior change
Comment #14
alexpott@andypost it's not about a protected method. It's about the inputs to a public method i.e. CsrfTokenGenerator::validate() resulting different behaviour. Given this is a a very security sensitive method I'd argue that maintain current behaviour of when exceptions are thrown or not is important.
Comment #16
catchAgreed with no behaviour changes here. Let's open a follow up to discuss refactoring things a bit.
Committed f2c21c1 and pushed to 9.1.x. Thanks!
Don't see a reason to backport this one, but please re-open if you disagree.
Comment #17
andypostFiled follow-up #3173720: Refactor CsrfTokenGenerator::validate()