# unserialize() data from untrusted source

- Thread: https://externals.io/message/12579
- Original archive: https://news-web.php.net/php.internals/12579
- Started by Harry Fuecks on 2004-09-05T15:29:53+00:00
- Messages: 7
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #12579 — Harry Fuecks — 2004-09-05T15:29:53+00:00

URL: https://externals.io/message/12579#12579

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

---

## #12585 — Jason Garber — 2004-09-05T19:33:28+00:00

URL: https://externals.io/message/12579#12585 · In reply to: #12579

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

---

## #12591 — Harry Fuecks — 2004-09-05T21:15:57+00:00

URL: https://externals.io/message/12579#12591 · In reply to: #12585

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

---

## #12601 — Markus Fischer — 2004-09-06T05:28:22+00:00

URL: https://externals.io/message/12579#12601 · In reply to: #12585

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

---

## #12602 — Sara Golemon — 2004-09-06T05:46:25+00:00

URL: https://externals.io/message/12579#12602 · In reply to: #12579

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

---

## #12609 — Jason Garber — 2004-09-06T08:38:03+00:00

URL: https://externals.io/message/12579#12609 · In reply to: #12601

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:
> > […]

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

---

## #12610 — Sascha Schumann — 2004-09-06T09:17:36+00:00

URL: https://externals.io/message/12579#12610 · In reply to: #12609

>  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
