Hi All,
Have a situation where I want to unserialize a string received from an
untrusted source over HTTP (a Javascript client in this case). For
basic types this is no concern but when it comes to objects, would be
nice to be able to restrict the class of object to a member of a known
list, to prevent "unplanned objects" being created from classes which
happened to be defined but were not intended for unserialization (such
as the growing number pre-loaded classes in PHP5), and the possible
security issues that might introduce.
Checking the type of class once the object has been created might be
too late, depending on what the constructor does. That leaves manually
parsing the serialized string in PHP, before called unserialize, as
the only really safe option.
Could be this is outside of the scope of intended use of unserialize()
but PHP's serialized representation of data makes a pretty nice
encoding for exchange with other systems and I notice others doing the
same e.g.;
http://www.aagh.net/projects/ruby-php-serialize
http://hcs.harvard.edu/~pli/code/serialPHP.pm
http://www.cpan.org/modules/by-module/PHP/JBROWN/php-serialization/
http://hurring.com/code/perl/serialize/
Serialized data is also often used with sessions which means the usual
issues with shared hosts and session files (OK - smarter users avoid
this but...)
Perhaps unserialize could take a second (optional) argument which is a
list of allowed classes to validate against.
Many thanks,
Harry
Hello Harry,
This is an interesting point you bring up. When we have large
registration processes or similar multi-page forms, we write our
data array to a hidden field using.
base64_encode(serialize($aData))
and read it in with
unserialize(base64_decode($_POST['aData']))
passing it from page to page with POST.
If I understand you correctly, someone could alter the content of
the serialized array so that it would load a class upon
unserialization? If you are auto-loading classes, this might be
even worse.
That being the case, I would be much in favor of an optional second
array parameter to unserialize that would be a list of accepted
classes, or an empty array to that would (obviously) allow no
classes (if you were working with simple data types).
mixed unserialize ( string str[, array classes])
I'd be interested to hear other comment on this.
--
Best regards,
Jason mailto:jason@ionzoft.com
Sunday, September 5, 2004, 11:29:53 AM, you wrote:
HF> Hi All,
HF> Have a situation where I want to unserialize a string received from an
HF> untrusted source over HTTP (a Javascript client in this case). For
HF> basic types this is no concern but when it comes to objects, would be
HF> nice to be able to restrict the class of object to a member of a known
HF> list, to prevent "unplanned objects" being created from classes which
HF> happened to be defined but were not intended for unserialization (such
HF> as the growing number pre-loaded classes in PHP5), and the possible
HF> security issues that might introduce.
HF> Checking the type of class once the object has been created might be
HF> too late, depending on what the constructor does. That leaves manually
HF> parsing the serialized string in PHP, before called unserialize, as
HF> the only really safe option.
HF> Could be this is outside of the scope of intended use of unserialize()
HF> but PHP's serialized representation of data makes a pretty nice
HF> encoding for exchange with other systems and I notice others doing the
HF> same e.g.;
HF> http://www.aagh.net/projects/ruby-php-serialize
HF> http://hcs.harvard.edu/~pli/code/serialPHP.pm
HF> http://www.cpan.org/modules/by-module/PHP/JBROWN/php-serialization/
HF> http://hurring.com/code/perl/serialize/
HF> Serialized data is also often used with sessions which means the usual
HF> issues with shared hosts and session files (OK - smarter users avoid
HF> this but...)
HF> Perhaps unserialize could take a second (optional) argument which is a
HF> list of allowed classes to validate against.
HF> Many thanks,
HF> Harry
This is an interesting point you bring up. When we have large
registration processes or similar multi-page forms, we write our
data array to a hidden field using.base64_encode(serialize($aData))
and read it in with
unserialize(base64_decode($_POST['aData']))
passing it from page to page with POST.
That's another scenario, and perhaps quite a common one, I hadn't
thought of. I guess in this case, as the serialized string was
originally generated by your code, you could attach a checksum with it
to spot alteration but validating classes against a list would be
easier. Checksums only really works though when your PHP script is
doing all the serializing; not an option when a remote, untrusted,
system will be doing the serializing.
If I understand you correctly, someone could alter the content of
the serialized array so that it would load a class upon
unserialization?
Exactly.
If you are auto-loading classes, this might be
even worse.
Good point - hadn't considered that.
That being the case, I would be much in favor of an optional second
array parameter to unserialize that would be a list of accepted
classes, or an empty array to that would (obviously) allow no
classes (if you were working with simple data types).mixed unserialize ( string str[, array classes])
There may be an issue with returned value, come to think of it. Right
now unserialize returns FALSE
(plus triggers and error) if it was
unable to parse the serialized string. Might be a requirement to be
able to identify invalid syntax from attempts to load an invalid class
(another callback or an exception?).
Jason Garber wrote:
This is an interesting point you bring up. When we have large
registration processes or similar multi-page forms, we write our
data array to a hidden field using.base64_encode(serialize($aData))
and read it in with
unserialize(base64_decode($_POST['aData']))
passing it from page to page with POST.
I fail to understand, in your scenario, why you don't simply save the
data in a session?
You're effectively generated some data server and send it to the client
only to get it back on the next request; typical session scenario, if
you ask me.
cheers,
- Markus
Hello Markus,
I've done both many times. There are many ways to do this type of
thing, but the way I described it is very clean because the data is
always in sync with the page (due to the fact that the data is on
the page).
If you save the data to a session, and then click back a couple
times, it can really mess with things, creating a much more complex
scenario to deal with.
As I said, over the years I've done both, and this is what I've
settled on as the most practical.
--
Best regards,
Jason mailto:jason@ionzoft.com
Monday, September 6, 2004, 1:28:22 AM, you wrote:
MF> Jason Garber wrote:
This is an interesting point you bring up. When we have large
registration processes or similar multi-page forms, we write our
data array to a hidden field using.base64_encode(serialize($aData))
and read it in with
unserialize(base64_decode($_POST['aData']))
passing it from page to page with POST.
MF> I fail to understand, in your scenario, why you don't simply save the
MF> data in a session?
MF> You're effectively generated some data server and send it to the client
MF> only to get it back on the next request; typical session scenario, if
MF> you ask me.
MF> cheers,
MF> - Markus
As I said, over the years I've done both, and this is what I've
settled on as the most practical.
Then you should have learned by now that verifying your
data's integrity is a mandatory task when designing any
client-side session system. Otherwise, attackers can inject
any kind of data into your system where the falsified data
will be viewed as 'trusted'.
Once you add the integrity check, you have also eliminated
the possibility that arbitrary classes could be instantiated.
- Sascha
Have a situation where I want to unserialize a string received from an
untrusted source over HTTP (a Javascript client in this case). For
basic types this is no concern but when it comes to objects, would be
nice to be able to restrict the class of object to a member of a known
list, to prevent "unplanned objects" being created from classes which
happened to be defined but were not intended for unserialization (such
as the growing number pre-loaded classes in PHP5), and the possible
security issues that might introduce.
if (preg_match_all('(^|:|{)O:\d+:(.?):', $serializedString, $matches,
PREG_PATTERN_ORDER)) {
/ Serialized data contains objects */
foreach($matches[1] as $match) {
$class = trim($match, "'"");
if (in_array($class, $bad_classes)) die("Bad hacker, no cookie for
you!");
}
}
Something along the lines of the above should do the trick, it's got some
shortcomings, but they're the type to give false positives rather than act
as security holes..... Though considering the fact that it'd be more
trustable/reliable to implement in unserialize itself and not a complicated
check to include anyway, I'd say "sure, why not".