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.
Related
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.
I was testing the exploit of a website.
The vulnerability is about PHP object injection.
The misuse of unserialize() may cause these problems.
Also, I found a warning in the official php manual as below:
They suggest that we should use json_encode() and json_decode().
However, I am wondering why json_encode() and json_decode() are more secure than serialize() and unsrialize()?
The difference is that serialize stores an object. If you change the contents of the string before unserialize you can trick the system to run malicious code.
With json_* functions you do not store the object, just the data. Upon "unpacking" the object has to be instantiated from original sources, and only then can be given json_decoded data to process.
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.
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
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.