New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
simplified cookies #14444
simplified cookies #14444
Conversation
Maybe it's still there from when there was a hash of the token (or something like this) stored in the cookie? Not sure why it was done. |
There is also my PR here #13301 which sets the domain correctly for the IgnoreCookie and disables the signature check for it to allow multiple matomo installations on the same domain to share it. Maybe these prs can be combined? If we can keep support for old 3rd party cookies and migrate them this would be great. |
@mattab do you maybe know more about this? |
Thanks @Findus23 for the PR! |
btw we probably still need to extract signed content because of existing cookies. |
@Findus23 any chance you could make it still work for old existing cookies? I suppose it won't have a big benefit then as you'd need to keep the unserialise anyway ... although without the salt an attacker shouldn't be able to do much there but of course you could brute force the salt etc. so it would only really help if we break existing cookie values which is maybe a no go @Findus23 @mattab ? |
@tsteur Hasn't this PR been mostly superceeded by #13301? |
@Findus23 I checked through cookie usage and I'm all for it. I'm only meaning existing cookies would break by changing it the way the PR is currently right? So we'd need to detect whether the cookie is signed or not signed. |
@tsteur @Findus23 I think we only have to be careful with the ignore and third party (pk_uid) cookie. For the ignore cookie the content doesn't matter only its presence, so I think we can safely change the cookie code without any additional work. However, since the third party cookie contains the global visitorid we should make sure that we still support reading third party cookies and auto upgrade them to the new cookie code. This will still break setups where multiple Matomo installations share the cookie and one of it is running an old version, but I guess that is acceptable. |
@Findus23 are you still planning to work on this one? We'd need to make sure to not break any existing cookies. |
@tsteur Shouldn't it be enough to just set https://github.com/matomo-org/matomo/pull/13301/files#diff-7de787015af7507a7278689396b18f7dR86 to false by default. This way it still splits the signature, but discards it. |
@Findus23 @MichaelHeerklotz I've merged the latest 4.X changes into this PR and tweaked the code so it's no longer signing content but it should still be able to read previously signed content. For BC I had to add back the previous I suppose now the content used to be signed so nobody could easily simply inject any kind of serialised information. Considering it's using Only unserialising if the string looks like it is serialised doesn't really help cause anyone could just make it look like that. Any thoughts? |
@Findus23 @sgiehl @MichaelRoosz be great to review this one. Newly set cookies won't use Existing content will be still read and use I did a basic test using old cookie format and new cookie format and things worked for me. Be great if more eyes could have a look at this. |
If it's too risky or not too important we could also simply close the PR. No preference. |
@Findus23 @sgiehl @diosmosis any thoughts on this one as the RC will be released soon? |
// sign cookie | ||
$signature = sha1($cookieStr . SettingsPiwik::getSalt()); | ||
return $cookieStr . $signature; | ||
$cookieStrArr[] = "$name=$value"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to not use json_encode? Don't think it really matters if this works, just wondering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably no particular reason I guess.
$varValue = safe_unserialize($tmpValue); | ||
if ($isSigned) { | ||
// only unserialise content if it was signed meaning the cookie was generated pre Matomo 4 | ||
$varValue = safe_unserialize($tmpValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
safe_unserialize
uses our custom unserialize code which doesn't use unserialize
at all, so I think this is fine? I guess it depends on how the cookie values are used to say if it's completely safe from injection. I guess we could also make sure there are only strings & numbers in the unserialized array to make it a little safer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK the most risk re safe_unserialize would be from when PHP actually does the unserialize. This risk should be eliminated with the safe_unserialize
(side note: I think since we now require PHP 7.2+ we could even use PHP unserialize with the additional parameter).
Not sure there's much benefit in checking it additionally. I guess with this PR people would need to know the general salt in order for this to be used so it shouldn't be much of an issue anymore anyway since newly signed cookies would no longer be signed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
safe_unserialize is defined in /libs/upgradephp/upgrade.php (included in bootstrap.php), and it's our code. I don't think it's PHP API? So should be super safe, I think. Unless we, use a variable in an insecure way, like modifying a session variable based on the index we get from the cookie or something like that. This is what I meant at least w/ my comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I meant eventually we can use https://www.php.net/manual/en/function.unserialize.php like unserialize ($str, false)
which I think should be the same as our safe_unserialize I reckon (but didn't fully check). PHP5.X didn't support the allowed_classes
parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imho it's fine to simply use Common::safe_unserialize
everywhere. It automatically disallows all classes, if non where given. Also it catches all errors and logs them. Should be simpler than doing that manually everywhere again...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just keep in mind that the safe_unserialize
in Matomo is over 10 years old and might have some weird bugs in it. (The upgrade.php seems to be only intended for PHP_VERSION<5.2
.
But it should be pretty safe as it only uses regex (unless someone finds an input value that creates a DOS).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. sure. Thought that was the method from Common class 🙈 We shouldn't use the safe_unserialize
from upgrade.php
. Should be Common::safe_unserialize
for sure!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, now that only PHP7 is supported we might be able to remove that upgrade.php completely. I'll see what happens when I try.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought initially as well it could be removed but reading https://www.php.net/manual/en/function.unserialize.php and the warning it says clearly regardless of any options value it isn't safe for user input (which it may be here) so prefer to maybe keep the existing safe_unserialise usage here for now. It should be mostly only for previously created cookies anyway. Unless someone had the general salt then they could still create any user content for cookie and sign it and it would use unserialize.
Code looks fine, doesn't seem like there would be any issues, BUT it might be nice to have this in an early beta and test it a bit more rather than include it in a big release like 4.0 w/ little testing. This isn't a breaking change either, so maybe it would be better to merge in 4.1? |
I have no preference. If there's an issue later it might not be discovered that quickly. Happy to move to 4.1 |
Well, the change looks good to me, so I guess it's up to you whether to merge it or not. |
I just realised the cookie::set method no longer accepts an array as value so added this to the developer changelog and we should merge it soon as it's a breaking change. I checked and it looks like we are only setting numbers and strings but no arrays. If needed people could always json_encode an array value themselves. @sgiehl maybe you can have a quick look as well if you spot anything? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test SessionInitializerTest::test_initSession_DeletesCookie_WhenAuthenticationFailed
is currently failing. Not sure why, but if I see that correct, that test actually never worked correctly before. The faked auth_cookie does not have signed content and thus it was discarded before resulting in a working test. Now as this unsigned cookie content is handled as valid (again), the test starts failing as a setCookie
actually does not erase the content in the cookie class. Guess sending a cookie using a Set-Cookie
header might not update the $_COOKIE
array, not sure if setCookie
would do that directly
Co-authored-by: Stefan Giehl <stefan@matomo.org>
I'm still utterly confused by how
core/Cookie
works in Matomo and I can't see any security benefit by "signing" the cookie and only lots of unused complexity by allowing arbitrary data to be deserialized.I could only find it used in:
makeThirdPartyCookieUID()
to save the$idVisitor
setLanguageForSession
to save the language code when anonymous userIgnoreCookie
for unknown reasons (but only for deleting cookies)SessionInitializer
which seems completly unusedso simply allowing to store a dictionary of strings should be more than enough for all use cases and far simpler.
This is more of a rough proof of concept to get my idea across, but I think this could be simplified in Matomo 4.
I'll probably append this PR draft with more similar commits.