views:

62

answers:

3

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!

+3  A: 

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?

Pekka
Good answer, completely forgot how `serialize()` worked.
Kristoffer S Hansen
The dangerous class would have a __wakeup() method that evals one of the object's properties and...
Cg Alive
And we have a huge library of classes and also an autoloader so, it's really hard to be sure nothing evil happens when the app is up! Although the system only accepts a fully encrypted string with it's key only visible to the app itself, but still, I'm not happy with where I'm putting my make-sure layer!
Cg Alive
@CgAlive If I manage to get an evil class definition into your project, why would I wait for some unserializing event to execute the evil code?
Pekka
You can't get the class in to our library (hopefully!) and there won't be any class that would like have any evil code like that.And the problem aint with evil, but with anything unexpected. Suppose an object opens a socket to connect with the outside world and provides some interaction functionality, and it's obvious it has to re-open the socket on __wakeup() and read the address from one of it's properties and that's how things start to turn messy!So an unserialize() could give us so many possible vulnerabilities, we can take DOS hits and many other things...
Cg Alive
A: 

I have never seen this attack vector in the wild. If you are expecting to get an array then you won't be calling $obj->method() on the results thus it will be difficult to influence the application.

However, you can use this simple function:

function safe_unserialize($s){
    $a=unserialize($s)
    if(is_array($a)){
        return $a;
    }else{
        return false;
    }
}

the __constructor() function might be called, i'm not sure about this and i can't test it right now. This shouldn't be a problem because in the vast majority of cases this is just initializing variables.

Rook
the function you show will already call the `__wakeup()` method of the object, which is what he wants to avoid
Pekka
@Pekka aah good call.
Rook
A: 

There are a couple of ways you could solve this problem:

  1. 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)
  1. 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.

  2. 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.

fx_
wow, thanks man! I was starting to think I'm turning into a paranoid programmer! Well I can't use JSON or anything else cause I need unserialize()'s speed. The funny thing is the serialized value is never supposed to get passed between client/server. It's stored in our secured db, and it's encrypted and also has a hash and salt and everything. But I need not to trust the db. I mean if one of our db nodes is screwed, I don't wanna see my whole app to go down with it. All the layers and components MUST have the security measures of their own!
Cg Alive