3

How do I securely save PHP objects in a MySQL Database? If you use the serialize and unserialize functions, you may end up with Object Injections. Is there a predefined standard on how to handle this?

Note that SQL injection =/= object injection! So PDO is a different story here!

Anders
  • 64,406
  • 24
  • 178
  • 215
Critical joe
  • 193
  • 1
  • 2
  • 9

3 Answers3

2

(Not) Trusting your database

You should be able to somewhat trust the data in your database, but as defense in depth it is definitely better to not trust it.

There may for example be a limited SQL injection, which allows inserting data into the database, but nothing else (maybe because your database rights are managed correctly).

But now, an attacker gained object injection, and who knows what that may lead to.

Data Integrity

You could use an HMAC to verify the integrity of your data. That way, you can be sure that what went into the database is the same thing that comes out of it (as long as an attacker doesn't have access to the key, which for example may be stored in the sourcecode).

Save Unserialize

PHP7 allows for an additional options parameter, in which you can specify what classes unserialize is allowed to create.

This already limits the power of object injection. An attacker can still overwrite fields in the object itself - which they can do anyways if we assume database write access - , which may not be harmful, but they cannot create arbitrary objects.

Input Validation

You could also perform some validation on the data before sending it to unserialize, such as collecting all O:X strings and checking if X is an allowed object.

But I would definitely recommend the save unserialize solution from above.

Mitigation: Not having exploitable classes and functions

It is a good idea to check data and authentication on a per-function level. For example, you shouldn't just assume that field x can be trusted because it isn't (currently) user-controlled, and thus eg safe to pass to passthru.

Not storing objects

If you do not have a good reason for doing so, you shouldn't store objects in the database.

tim
  • 29,018
  • 7
  • 95
  • 119
  • Thank you for the answer! Very clear! However, why is it not a good thing to store objects in the database? My teacher (which is apparently not a good source) said it would be better to manage data consistently. – Critical joe Aug 20 '16 at 14:35
  • 1
    @Criticaljoe Because MySQL is a relational DBMS and misusing it as object DBMS doesn't make sense (if that is desired, it's better to just use an object DBMS). One problem is that you decrease your options, because all entries in your database will be a giant blob + an id. You can't use SQL for anything except to fetch objects now, you can't use features such as constraints or searches, you always have to retrieve your entire objects, and you can't use anything other than PHP to access your data. You will also run into consistency problems in case a class changes. – tim Aug 20 '16 at 14:49
  • 1
    @Criticaljoe I would definitely not recommend directly storing objects in MySQL and I wouldn't consider it best-practice, but there may be something that I'm missing, so I asked a question about it at [programmers.SE](https://programmers.stackexchange.com/questions/328874/dis-advantages-of-storing-objects-in-relational-database), in case you are interested. – tim Aug 20 '16 at 16:36
1

If you don't trust your database then object injection is probably the least of your worries. The link you supplied specificaly relates to user-supplied data. It is a very unusual case where you will receive PHP serialized data from a client outside your application stack.

The solution is simply, and as always, to check that user-defined data falls within acceptable boundaries. While you could use a wakeup magic method to check for unexpected object injection (slightly simpler than implementing your own unseralizer) a better solution is to maintain a signature of the serialized data and very this before unserializing:

 define('SERIALIZE_SALT', 's3cr3t5auce');

 function signed_unserialize($serialized, $signature)
 {
     if (sha1($serialized . SERIALIZE_SALT) === $signature) {
        return unserialize($serialized));
     } else {
        whatever();
        return new stdClass();
     }
    }

However I think I should point out that using a relational database to store opaque objects (and for that matter, trivial ORM) results in applications which are difficult to maintain and very difficult to scale. These problems, and your original issue are addresed by normalizing your database and implementing factories in your application).

symcbean
  • 18,278
  • 39
  • 73
1

Object serialization in PHP is a mine field that you better stay out of. The manual comes with a big red warning:

Warning! Do not pass untrusted user input to unserialize() regardless of the options value of allowed_classes. 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. Use a safe, standard data interchange format such as JSON (via json_decode() and json_encode()) if you need to pass serialized data to the user.

If you need to unserialize externally-stored serialized data, consider using hash_hmac() for data validation. Make sure data is not modified by anyone but you.

You should always regard data from the database as untrusted data. Hence you should not call unserialize on it. The HMAC solution referenced above would work if done correctly, but it is very easy to get the details wrong here.

So my advice is to just block that evil unserialize function in your php.ini, and go with JSON instead.

Anders
  • 64,406
  • 24
  • 178
  • 215