PHP/deserialize - is it safe to deserialize $_GET value? - php

I'm passing urlencode()d serialize()d arrays around my webpages, via $_GET[].
Is it safe to deserialize() a value from $_GET? The deserialized array will sometimes be shown to the user. Would it be possible for a user to expose/reference variables or functions etc within my code? In other words, when deserializing the value, does PHP treat it as data or code?
Update:
I see the documentation says:
"Circular references inside the array/object you are serializing will also be stored. Any other reference will be lost. "
So that means i'm safe? :-)

Absolutely, positively, no.
You shouldn't blindly trust anything from the client side, however there is a way you can give yourself more confidence.
I'm assuming that if you've got PHP serialized data coming from the client side, that client obtained that from a server at some point? If that's the case, and the client doesn't modify the data, you could include a hash along with the data to verify it hasn't been tampered with.
The other alternative would be to unserialize the object, but regard it as 'tainted', then copy and re-verify the unserialized data into a 'clean' object.

This method is as "safe "as any other kind of incoming GET or POST data - you will always need to sanitize the data before working with it! But there are additional issues with unserializing user data.
When unserializing an object, PHP will look whether the class has a __wakeup magic method. That method will get executed if present.
Now this is not a massive security hole in itself, because the class definition is never transmitted in the serialized data. Any malicious code would have to be present in the system already. However, there are conceivable scenarios where this could be a problem (e.g. a plug-in system that can install third party code) and I would be very wary with this.
Also, theoretically, this allows an attacker to create an object of any class inside your script. While not a security problem straight away, it is surely not good practice to do.
JSON encoding would be a more safe way, because it can contain only "dumb" data.

You are serializing only data-part of objects/arrays/variables, the actual executable code is not serialized- there is no point in doing that - serialization helps to transfer your data between two different worlds- executed code can be same or different there - for data it does not matter.
Though possible hacks would be possible - but only based on data - classes and types and values might differ - it's up the code how can it cope with errors during deserialization.

Yes, its safe. You are asking is it safe to serialize the value of the $_GET array. Yes, it is safe. Nothing gets executed during the serialization of array. Since $_GET array does not contain any objects, only the parameters from query string, it cannot do any harm during serialization/unserialization.
You mentioned something you saw on documentation about circular references. Don't worry about that, it does not apply in your case because there are no objects inside the $_GET array.
As far as using the actual data from the $_GET array, that's a different question and the answer would be no, it's not safe to use data from the $_GET array without applying some type of filter or validation first

Related

Is it safe to serialize and unserialize the $_POST array?

TLDR: Is it safe to serialize the $_POST array to a file and then read it back into the $_POST variable on a different request or a different script?
I realize this is unusual. There is a reason for it, and it would take a dozen pages of text to explain why I am considering doing it in a special case, at least in the meantime.
Boiled down process:
file_put_contents('sample.post', serialize($_POST));
$_POST = unserialize(file_get_contents('sample.post'));
I already have extensive filtering in place for the actual contents of the post variable. My question is whether or not the process of serializing and unserializing the entire $_POST array is giving a malicious user a method of attack.
The PHP doc says "Warning. Do not pass untrusted user input to unserialize(). Unserialization can result in code being loaded and executed due to object instantiation and autoloading, and a malicious user may be able to exploit this."
I found these articles that describe this method of attack. But they seem to depend on the user being able to specify the string to unserialize directly, IE unserialize($_POST['value']).
https://www.notsosecure.com/remote-code-execution-via-php-unserialize/
https://heine.familiedeelstra.com/security/unserialize
Am I correct that as long as I am serializing and unserializing, objects can't be created in the unserializing process, right?
I am under the impression that the $_POST array will only ever contain strings (though I couldn't find that explicitly mentioned in the PHP docs).
As I understand it, even if someone supplies a string matching the format of a serialized object, it will be 'stored' as a string during serialization, with an explicit length (byte length). So it will just be assigned as a string when unserializing. It seems like since the lengths of the strings are stored along with them during serialization, you can't break the structure of a serialized string from the input like you might with SQL injection. I tried to trick it with some invalid multi-byte characters, but no luck. However, me being unable to do it and experienced hackers being able to do it are 2 different things.
I couldn't find anything else about other attack methods.
Please let me know if I'm missing something! I just read several comments saying 'you should never do this', so I'm nervous that I'm misunderstanding something.
I think it is not possible without further attacks to send something as a POST variable to exploit the unserialize() call later in your scenario, but others may probably have an idea. It could be a problem if $_POST had something unserializable, but I think that may not happen. Whatever was in $_POST, it was already in memory once, so assuming serialize() and unserialize() work correctly, it should be secure to do something like
$serialized = serialize($userinput);
unserialize($serialized);
However, you are saving this data to disk inbetween. After exploiting a different flaw and having access to your files (or having access "by design", like IT ops staff), an attacker may be able to modify saved serialized data, and may inject his attack there. That way it would obviously be vulnerable.
So it is indeed a risk, albeit maybe not a very high one. Be aware that the security of this solution depends a lot on security of your saved serialized content.

How to avoid object injection from cookies?

I need to store structured data in a cookie.
favorites of some catalog page. the data is { {id,type} , {id,type} , ... }
In my code i need to put it back to a an array. so i am using the unserialize() function to recover the data.
Is there a way to prevent object injection? any alternative for the unserialize function?
Is there a way to prevent object injection
When you unserialize an object, PHP will call the __wakeup method in the class' definition (this is not stored in the cookie but in your PHP class file on the server).
Would it be possible for someone to change the serialized object's class and guess correctly causing a __wakeup function to perform some task? Yes. Usually __wakeup functions will simply reconnect to a resource so this may or may not be a big deal but why risk it? Just store the array as JSON with json_encode($yourdata) and get it back with json_decode($cookiedata, true)
As long as the data being stored client-side does not pose a security risk (i.e., reveals sensitive information) it's fine. Storing a user's favorites can be appropriate for this, especially if it's a high traffic site and you don't want to have to perform a database fetch or long sessions for this type of data.
Well, if you have a defined structure of how your array should look, then just make sure that the string you're unserializing is really an array when you're iterating over it just check the value type (id -> int, type -> string).
Plus, I think json_encode will be better in your case of data, it is smaller in size and faster in decoding (and you can work with it on the client-side).
The way I'm thinking about would be to encrypt the cookie information, to prevent the user from knowing what the hell is he/she editing
If you don't want the user to potentially modify the data, then don't give the data to the user in the first place. Use $_SESSION, a database, or write the info to a file on your server.

Is it safe to unset PHP super-globals if this behavior is documented?

I'm building a PHP framework, and in it I have a request object that parses the url as well as the $_GET, $_POST and $_FILE superglobals.
I want to encourage safe web habits, so I'm protecting the data against SQL injection, etc.
In order to ensure users of this framework are accessing the safe, clean data through the request object, I plan to use unset($_GET, $_POST, $_REQUEST); after parsing those variables.
I will document this in the method comments, and explain in the framework documentation that this is happening.
My question is: Would this be desirable behavior? What are the potential pitfalls that I have not foreseen?
I know this was answered already, but here's my $0.02.
I would not unset or clear the input arrays. However, what I have done is to replace them with an object. So instead of having the raw array, I replace it with an object that implements ArrayAccess and Iterator. That way the vast majority of code which uses the native arrays will still work quite well with the object.
The rationale is that at least you can verify that the code paths are operating correctly via tests. You can replace those objects with a mock object to throw an exception during testing so that you can detect improper access to those arrays (if you do determine it to be "bad practice"). So it lets you run during production without putting un-necessary restrictions, but also lets you turn it on to verify best-practices during testing.
And while I do agree with #JW about escaping, you should be filtering input. Filter-in, Escape-out. Any time data comes into your program (either via user input or from a DB), filter it to expected values. Any time data goes out (either to the DB or to the user), you need to properly escape it for that medium. So using a request object that enables easy filtering of the submitted data can be very valuable.
An example using a fluent interface (which you may or may not want):
$id = $request->get('some_id')->filter('int', array('min' => 1));
And that doesn't include the benefits of compensating for differing platforms and configurations (for example, if magic_quotes_gcp is enabled or not, etc)...
Anyway, that's just my opinion...
I'm not sure what the point would be of preventing access to the $_GET or $_POST arrays. There's nothing harmful in them. If you're creating a framework for preventing SQL injection or cross-site-scripting, you should escape the data when creating an SQL query or HTML document.
Escaping GET/POST data at the beginning is too early; you don't know how the data will be used, so you can't escape or encode it properly.
Having said that, you still may have some valid reasons to want people to access GET/POST data through your code. In that case, I still wouldn't unset them. You may end up incorporating third-party code which relies on them. Instead, just encourage your users to avoid them (like they should avoid global variables in general).
I'd maybe expose a method (maybe hidden or super counter-intuitive ;)) to get the raw data, in the off chance that your sanitization routines corrupt data in some unforeseen manner. To protect the user is one thing, but to completely lock them from their ability to retrieve data in the most raw manner may lead to frustration and, as a result, those people not using your framework :)
Keep in mind this increases your maintenance costs...if anything is ever added, removed or changed with the super globals in PHP, you will need to update your framework.
Sounds like magic_quotes style thinking. Except, at least magic_quotes was 99% reversible at runtime. Your "cleaned" data might be lossy, which really sucks.

Limiting unserialize() to return arrays?

Is there any way to limit PHP's unserialize() to only parse arrays?
For security reasons. Suppose there is is an evil __unserialize() magic method in the unserialized object I don't wanna call!
Is there any way to limit PHP's unserialize() to only parse arrays? For security reasons. Suppose there is is an evil __unserialize() magic method in the unserialized object I don't wanna call!
Not that I know of, no.
It is possible to find out the type of a serialized value using a function like this one, but that won't help you either, as any member of the array could again be an object whose unserializing will trigger a __wakeup() call.
You would have to extend that function so it walks through all the members of the serialized string without actually serializing it. Certainly possible, but potentially kludgy and slow.
The only other way that comes to mind is to make the unserialize() call in an environment in which no classes are defined. That will result in a broken object of the class __PHP_Incomplete_Class that you may then be able to parse out. In a normal script environment, this will however not help you.
That said, never forget that a serialized object will never contain any code. The class definition will have to be present in your code base through other means.
In light of that, I'm not sure under what circumstances this can be a security problem in the first place. If you have malicious code in your code base, there will be plenty of chance to execute it without having to unserialize anything, won't they?
There are a couple of ways you could solve this problem:
Use a regex on the serialized string:
Piwik patched an unserialize vulnerability with the following check:
if (preg_match('/^a:[0-9]+:{/', $str)
&& !preg_match('/(^|;|{|})O:\+?[0-9]+:"/', $str)
Sign the signed string. If you add an sha1 Hash of the serialized string + a secret to the Cookie/POST Var. You can be sure that the serialized string isn't manipulated.
Write your own unserialize function. If you are only interested in Arrays, you don't need unserialize. Write something on your own or use an accepted standard like JSON etc.
And please ignore all comments that don't see a security issue here. unserialize() vulnerabilites exists in an incredible high percentage of PHP5 Applications and most books and tutorials don't even talk about them.

Does PHP's $_REQUEST method have a security problem?

The textbook I read says that $_REQUEST has security problem so we better use $_POST.
Is this OK?
I would say that it is dangerous to characterise $_POST as more secure than $_REQUEST.
If the data is not being validated and sanitized before being used, you have a possible vector of attack.
In short:
It doesn't matter where the data comes from if it is not being handled in a secure manner.
Well, the reason that $_REQUEST has issues is that it picks up values from $_GET, $_POST, and $_COOKIE, which means that if you code things certain ways and make certain invalid trusting-the-client assumptions, a malicious user could take advantage of that by supplying a value in a different place than you expected and overriding the one you were trying to pass.
This also means that you may have given your henchman incorrect instructions, because it may have been a GET or COOKIE value that he was picking up from $_REQUEST. You would need to use whatever place the value you're looking for actually shows up, not necessarily $_POST.
As was mentioned already in several answers: Any data coming from the client cannot be trusted and must be treated as being malicious by default. This includes $_POST, $_GET, $_COOKIE and $_REQUEST (the combination of the former) but others as well.
When talking about some of them being more dangerous than others I would indeed separate $_GET and $_REQUEST (as it includes $_GET) out from $_POST, as it is slightly harder to generate, i.e. manipulate, a POST request than a GET request. The emphasis here is slightly, but using POST for sensitive operations at least removes another layer of low hanging fruits to exploit.
Especially when it comes to Cross Site Scripting (or XSS) and cookie theft, it is rather easy to get a victim's browser to issue a GET request to the server under attack by simply inserting a hidden image with a manipulated URL into a page or forging a link.
Issuing a POST request at least requires some JavaScript, which is slightly harder to inject into the victim's browser for execution (depending on the situation). Obviously POST requests can be generated by attackers directly, so they can't be trusted either, but for scenarios where an attacker is going through a 3rd party browser, they are a little bit harder to manipulate.
Security is always about making it as hard as possible to break your application - taking implementation constraints etc. into account. It can never be about being 100% secure. So it's best practise to choose the alternative which is more difficult to exploit, even if the difference is marginal, when having the choice between different implementation approaches.
In the end it is always about removing low hanging fruits. Sure, POST requests can be manipulated as well, but for any operation that has an elevated risk, use a POST request and restrict yourself to using $_POST in your code. That way you have have already excluded some very easy GET drive-by attacks and can now focus on validating your POST data. Just don't assume that using POST suddenly made the operation safe by default.
It's certainly okay to tell people to use $_POST instead of $_REQUEST. It is always better to be more sure about where your getting your data.
#Christian:
When talking about some of them being more dangerous than others I would indeed separate $_GET and $_REQUEST (as it includes $_GET) out from $_POST, as it is slightly harder to generate, i.e. manipulate, a POST request than a GET request. The emphasis here is slightly, but using POST for sensitive operations at least removes another layer of low hanging fruits to exploit.
Bzzt. Sorry, but this just ain't true.
Anybody who understands the difference between GET and POST or how unsanitized inputs might be exploitable, won't hesitate for a second to fire up Tamper Data.
Some people have it right here: there is NO security lost or gained by using $_REQUEST in a well-designed system.
There's no real security difference between using $_POST and $_REQUEST, you should sanitise the data with equal scrutiny.
The biggest problem with $_REQUEST is you may be trying to get data from a POST'd form, but might have a GET parameter with the same name. Where will the data come from? It's best to explicitly request the data from where you expect it, $_POST in that example
There are slight security benefits - it's easier to perform XSS (more specifically XSRF) attacks on GET parameters, which is possible if you use $_REQUEST, when you really just want POST data..
There's very few situations when you need data either from POST, GET or cookie.. If you want to get POST data, use $_POST, if you want to get data from from GET parameters, use $_GET, if you want cookie data, use $_COOKIE
The most secure way is to verify and validate the data. I usually generate a random unique id for a form and store it in the user's session, but this is easily bypassed by a determined attacker. Much better is to clean up all incoming data. Check out htmlspecialchars() and its related function. I also use a third party utility for cross-site, like HTML Purfier
On some practical notes, always use intval() what is supposed to be numeric, escape all incoming strings, use regex for phone numbers, emails or anything that is going to be part of a SQL query.
Hope this helps.

Categories