-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
PHP Object Injection Vulnerability in SessionCookie.php #1034
Comments
Per feedback from @joepie91 -- anyone worried about large volumes of session disk space usage can turn this feature on and turn the number down
Could be worth changing this class to store data in JSON format? |
Why are you storing session state in the hand of the client in the first place? I'd hate to be self-referential, but see similar work I've done for Kohana and CodeIgniter: https://scott.arciszewski.me/research/view/php-framework-timing-attacks-object-injection They had a similar problem, except they were using hash functions (poorly) to detect tampering. It's still a bad idea. Use server-side storage:
Cookies are doom. |
Totally agree, but some people may need them for some reason. They're good for long-term client-side configuration storage, no need for objects though, a key/value pair should be enough. Definitely not for use with secure data. |
Okay. Who needs them and what are they doing that requires such a poorly designed feature? PHP supports file storage out of the box.
People are going to use sessions for secure data. |
How do you feel about storing such data in a cookie as an encrypted JSON Web Token? |
NO#1035 - I do not trust the framework authors to implement encryption properly after reading |
@sarciszewski But how do you really feel? I agree earlier versions should not persist session data client side. This will be changed in 3.x. You can also review the latest encryption strategies in \Slim\Crypt in the develop branch. |
@sarciszewski Please review the develop branch \Slim\Crypt class and let me know if you have any feedback. I want it peer reviewed as much as possible. It's already had a lot of eyes on it, but one more won't hurt. Thanks! |
Like what reasons, for example?
That's what non-session cookies and local storage are for. This does not belong in a session, end of story. |
I wasn't necessarily referring to the context of slim. https://github.com/firebase/php-jwt/blob/master/Authentication/JWT.php uses PHP's built in hmac and openssl stuff. Why wouldn't one just use a third party library? It becomes as simple as, pass it data to JWT encode, pass it a key for encryption. Not sure I follow your concern. You seem to suggest they use someone else's php encryption library to solve that case. Wouldn't that be fine for JWT? |
As I believe was already covered, it does make things more scalable. That is, I think I'd agree with the principle as described in REST, and the client should contain the necessary state to succeed in a given request. Sessions blur this, and if the session is removed, the client state is affected irreversibly. This is true for memcache as well. It scales horizontally, but the client application state is still dependent on the state of the server... clear memcache, and the client is suddenly logged out. EDIT: Even if you mistook me, I agree that "session data" such as a login name can and should be stored client side. It's part of the client state. I agree, this means we need a means to do so securely, but the answer to that is not "move the client state to the server." |
I don't really see how this makes it more "scalable". You may as well run a Redis cluster to keep track of sessions, if you're at the scale where you need multiple backend servers - statefulness doesn't change anything here. And frankly, if you're running at that kind of scale, you don't need to rely on the default framework configuration, and can easily roll your own session handler - they are pluggable, after all. Hell, even if you can't, there's plenty other solutions like sticky sessions. Right now, Slim is shipping in an insecure default for the majority of users, in order to solve an effectively non-existent problem that is an edge case at best. People are not going to read that caveat about secure storage, and cookie encryption is almost certainly going to cause issues later down the line. Not to mention the size limits that are going to bite people. TL;DR: It makes absolutely no sense to implement this in a framework like this. |
It makes it more scalable for the same reasons redis/memcache/session handler does in one sense, but also makes it more scalable in the sense that client state can be provided to other systems or services which may not be tied into the back end state. Sure, it's easy to say, "just tie them into the backend state," but that doesn't seem to really address the issue. What you're saying is, X service now has to rely on some additional backend component in order to verify client state. Not only might that be completely unnecessary for some services (you're only making it necessary by mixing server/client state), it obviously increases the requisite resources to make that service usable. When the client state is stored client side and passed in the request, any service, no matter how disconnected, has everything it needs to complete the request. It's really that simple. |
I should probably note, I'm not necessarily disagreeing with you with respect to slim. I'm more making the point on a broader architectural level. I would agree that at that scale you're going to be doing custom stuff anyway, and this may not be necessary out of the box, and as implemented, causes all sorts of security concerns for the 90%. |
Cookies are hostname- and port-specific, so that will never be directly possible. Data that you want to share between systems should be explicitly shared (even when going through the client) - you can use things like local storage to keep track of this. There's a fundamental distinction between 'trusted data' and 'shared data', and the latter simply doesn't belong in session data. Non-session cookies or local storage are a much better choice for that. |
This is irrelevant with respect to some hierarchical architectures or even more advanced proxies.
I think you meant the former simply doesn't belong in client data. But that's an obviously absurd notion for every single application architecture aside from web applications. The only reasons it's accepted in web applications is because of arguments like the one you're making, namely that there's just no way to do it securely. All I'm saying is, the "how do we do this securely?" is a separate question from "why/when/should someone do this?" There are cases for the latter, which means we need solutions for the former. You seem to be saying that there are no solutions for the former, thus, we should do whatever it takes to avoid the latter. You've got your problem solving hat on backwards. All I was initially asking in this thread, was whether or not @sarciszewski felt JWT was a relatively secure method to store session data client side (regardless of mechanism, cookie, local storage, etc). His answer was NO, but it seemed specific to separate concerns in slim, which didn't really answer the question. |
I said "NO" because you shouldn't be storing session state that the server processes and depends on, on client machines. An identifier to fetch it from the backend? Sure. Even with JWT, you're going to run head-first into the 4KB limit (which is down to 3 KB with base64 encoding, and even less with the HMAC). You're literally cutting yourself off at the knees with this strategy. Want to make everyone insecure because a few morons think storing session state on the client is okay? Do what you want; I look forward to seeing your spools on Full Disclosure. |
No, I meant it doesn't belong in session data, exactly as stated. You can keep state beyond session data, including on the client.
I still have to see them. Can you name one for me? |
This sounds more like a lack of proper deliniation between server state vs. client state. Based on the source you cited in OP, I'd agree. I was moreso referring to...
So it sounds like you're OK with this, if implemented in a secure fashion.
In this specific case, I'd think that would depend on how much you're storing in session, which again, may be an issue of not properly dilineating.
No. But I agree that is what this particular example does. |
Yes, like |
Then (as with my previous comment to @sarciszewski), this sounds more like a deliniation issue.
I have no idea what this means in the context of the quote you quoted. Can I name one what? Are you asking if I can show you a specific example of an application running at that level of scale which benefits from client state actually being stored in the client? No. I don't know of a single one that's open source. Do they exist? Obviously, there were plenty of examples when I worked in Motorola. |
@mattsah Apologies, mis-quoted. Quote should have included:
I'd like to see one of those cases. |
This is an avoidance of the issue, not an addressing of it. Again, no clear delineation between client state and server state. They're on one extreme (all server state is client state) and you're on the other (all client state is server state). Specifically, I'm talking about storing a user token with information as to which user the client is authenticated (or even whether they are at all). |
I believe I responded to your statement as if that's what you were responding to. So, no, I have no publicly visible examples. They may exist, but I don't know every enterprise level web application that is open source. Maybe Bacula? |
Congratulations, you just reinvented PHP's session storage. |
Sessions are stored serverside. |
Yes, the data is stored serverside, but the identifier is stored in a cookie. |
I agree with @ircmaxell. We wouldn't be discussing any of this if SessionCookie middleware used json_decode to retrieve the key/value pairs of the encrypted cookie. The cookie is encrypted to avoid someone tampering with its values, but it isn't meant to hold anything besides your logged status and user_id. Sending this hash on each request, and having the server unencrypt it to know who you are is just as secure as sending an OAuth token to an API backend. Sure, you can tamper with an OAuth token just as easily as you can tamper with a cookie. Hell, you might send whatever token you want and if you succeed in forging a token that amounts for another person you can impersonate them. Because that's how it works, actually. |
I still see a problem here. Encryption is not authentication. Since the session state is in the hands of the client, they get unlimited tries. It takes an average of 128 tries (worst case: 256) per byte to break CBC and forge the contents. Encrypt-Then-MAC or doom.
Wait, is it encrypted or hashed? @ircmaxell I see your point. If they can implement JWT competently (my history with other frameworks reinvention the session wheel has not made me optimistic about this) this issue can be considered closed. |
Yes. That's a good assessment, given that an actual compromise using unserialize() could result in arbitrary code execution on __wakeup() if I'm not mistaken. |
Yeah, that's the crux of this issue. |
@sarciszewski just a point to note and for the slim team, JWT still requires separate encryption, initially it's just a hash with signature if I'm not mistaken. The data itself still needs to be encrypted. |
@mattsah If you use Firebase's implementation rather than rolling your own, then I won't complain. (Instead, I'll go review that.) EDIT: Their library looks good at a glance. |
@sarciszewski I'm not on the slim team. If you'd like to review Firebase's implemention, go for it, but please take note of the above with respect to encryption. Firebase, so far as I know is just supplying stock JWT which gets around the unserialize()/__wakeup() issue and offers some data integrity. Encryption is still needed. |
Well, the develop branch has adopted |
@sarciszewski so basically the problems are
I agree on those too, but that has no relation whatsoever with the pros or cons of using user provided data (cookies, headers, query string or POST params) to provide a stateful app. |
If you're fine only being able to store up to 3022 bytes of information in your session state of information per user ever, then by all means keep at it. I'd recommend adopting a more modular approach. Let people choose their session backend, don't lock them into the cookie-based one. If they're fine storing it the default PHP way, it should be trivial to do so. EDIT: Also, please don't use compression to try to get around this. https://www.isecpartners.com/blog/2012/september/details-on-the-crime-attack.aspx |
But Slim does by no means force you to use SessionCookie middleware. The default implementation uses native session storage. |
That wasn't obvious to me when I was looking at the code. |
Oh, thank you. This is probably a lot less critical than I imagined, then. |
Just wanted to chime in here. First, I truly appreciate the scrutiny here. I really do, be it overly harsh or not. It only makes Slim better. Also, the 2.x code is not as up to date as I'd prefer. Neither is its documentation. I've been on hiatus so to speak for 1.5yrs... working on other things, writing a book, etc. But now I've returned and am moving fast on the upcoming 3.x release (the current develop branch). You can read details about the upcoming release here: http://www.slimframework.com/2015/02/11/whats-up-with-version-3.html It's getting better daily, and I'm trying to fix any issues as they appear. Most notably, I killed Slim's naive crypto implementation tonight and replaced it with Zend/Crypt. Anyway, thanks for the scrutiny. As for the 2.x code, if you see anything that needs improved, fixed, etc. please send a pr against the master branch. This branch is only accepting hotfixes and security updates. |
Okay, that's basically the best response I could have asked for.
I'll poke around. In the meantime, swapping out unserialize()/serialize() for the appropriate JSON features will stop the PHP Object Injection (which can result in remote code execution) vulnerability and you can focus on implementing JWT in develop. EDIT: I've also edited the issue message to clarify my misunderstanding. |
@codeguy I believe that there might be a problem when dealing with non-english characters (and storing a person's first or lastname in the session might be fairly common) resulting in null values. A safer solution could be
|
Suggest avoiding that issue altogether by simply mandating that storage be UTF-8 in the first place. |
@ircmaxell Would this do the trick? $from_type = mb_detect_encoding($data, 'UTF-8', true);
if ($from_type !== 'UTF-8') {
$data = mb_convert_encoding($data, 'UTF-8', $from_type);
} (I'm going to guess that the answer is "no", but I'm curious why it won't. Maybe I should make a SO post?) |
No. Encoding detection is incredibly unreliable at best. |
I'm not aware of the inner defects of utf8_encode, but your comment sure makes me want to dig deeper on that. However, replacing serialize with json_encode in the master branch might result in current users of Slim 2.5.x seeing their cookie persisted session returning null. Since json encoding non utf8 values doesn't throw any errors, they will be in a difficult position to debug the unexpected behavior. They might spin in circles before actually trying json_last_error(). |
Okay. |
@amenadiel In fact, all text is valid LATIN-1. This leads to the problem where codepoints are converted very weirdly (especially if source was UCS2-le - what MS likes to use in their OS's). So significant information can be lost, as it transcodes the 127 LATIN-1 high-point characters to UTF-8. The reverse is even more damaging, since it tries to turn 2^21 into 2^7 (2^21 being approximately the number of possible UTF-8 codepoints). |
However unreliable encoding detection may be, having your session data a bit scrambled is easier to debug than null data. I'm really concerned on the consequences of rolling this change as is in the next minor release. |
Enjoy getting 0wned then. Maybe wrap non-UTF8-encoded data in |
* upstream/2.x: (33 commits) Updated version number references for upcoming Slim 2.6.3 Clean up the output buffon on error in debug mode Updated version number references for Slim 2 Updated the other code snippets as well Added color to the Readme Update How to Contribute section of README fixed broken license link Update SessionCookie unit tests for new JSON decoding Fix the build indicator. escape html code in exception page update version number in tests folder update version numbers Fix slimphp#1034 (CVE-2015-2171) Correct terminology in README travis: PHP 7.0 nightly added Fixes slimphp#962, Missing SERVER_PORT on GAE. Add HTTP status codes from new RFCs. Update index.php to fix broken "Hello World" link Fix: Method doesn't return anything Fix: Undefined classes ...
https://github.com/slimphp/Slim/blob/master/Slim/Middleware/SessionCookie.php#L127
Generally, it's a bad idea to blindly
unserialize()
user-controllable input.https://www.owasp.org/index.php/PHP_Object_Injection
EDIT - for people who don't want to read the whole thread:
The SessionCookie class is not used by default, you have to actually write your application to use it. So this means that the unserialize() -> RCE possibility is only for the select few apps that explicitly use this feature. The default is the native session driver, which is of course not vulnerable.
The text was updated successfully, but these errors were encountered: