Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Merge pull request #37 from MatthiasKuehneEllerhold/unserialize-white…
Browse files Browse the repository at this point in the history
…list

Unserialize whitelist
  • Loading branch information
weierophinney committed May 14, 2018
2 parents 8e43a63 + 4423d9c commit 643e8a6
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 5 deletions.
7 changes: 6 additions & 1 deletion docs/book/adapter.md
Expand Up @@ -16,7 +16,12 @@ The `Zend\Serializer\Adapter\PhpSerialize` adapter uses the built-in
[serialize()](http://php.net/serialize)/[unserialize()](http://php.net/unserialize)
functions, and is a good default adapter choice.

There are no configurable options for this adapter.
Available options include:

Option | Data Type | Default Value | Description
--------------------------- | ----------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------
unserialize_class_whitelist | `array` or `bool` | `true` | The allowed classes for unserialize(), see [unserialize()](http://php.net/unserialize) for more information. Only available on PHP 7.0 or higher.


## The IgBinary Adapter

Expand Down
49 changes: 47 additions & 2 deletions src/Adapter/PhpSerialize.php
Expand Up @@ -3,12 +3,13 @@
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Serializer\Adapter;

use Traversable;
use Zend\Serializer\Exception;
use Zend\Stdlib\ErrorHandler;

Expand All @@ -21,8 +22,15 @@ class PhpSerialize extends AbstractAdapter
*/
private static $serializedFalse = null;

/**
* @var PhpSerializeOptions
*/
protected $options;

/**
* Constructor
*
* @param array|Traversable|PhpSerializeOptions|null $options
*/
public function __construct($options = null)
{
Expand All @@ -35,6 +43,36 @@ public function __construct($options = null)
parent::__construct($options);
}

/**
* Set options
*
* @param array|Traversable|PhpSerializeOptions $options
* @return PhpSerialize
*/
public function setOptions($options)
{
if (! $options instanceof PhpSerializeOptions) {
$options = new PhpSerializeOptions($options);
}

$this->options = $options;
return $this;
}

/**
* Get options
*
* @return PhpSerializeOptions
*/
public function getOptions()
{
if ($this->options === null) {
$this->options = new PhpSerializeOptions();
}

return $this->options;
}

/**
* Serialize using serialize()
*
Expand Down Expand Up @@ -85,7 +123,14 @@ public function unserialize($serialized)
}

ErrorHandler::start(E_NOTICE);
$ret = unserialize($serialized);

if (PHP_MAJOR_VERSION >= 7) {
// the second parameter is only available on PHP 7.0 or higher
$ret = unserialize($serialized, ['allowed_classes' => $this->getOptions()->getUnserializeClassWhitelist()]);
} else {
$ret = unserialize($serialized);
}

$err = ErrorHandler::stop();
if ($ret === false) {
throw new Exception\RuntimeException('Unserialization failed', 0, $err);
Expand Down
52 changes: 52 additions & 0 deletions src/Adapter/PhpSerializeOptions.php
@@ -0,0 +1,52 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Serializer\Adapter;

use Zend\Json\Json as ZendJson;
use Zend\Serializer\Exception;

class PhpSerializeOptions extends AdapterOptions
{
/**
* The list of allowed classes for unserialization (PHP 7.0+)
* Possible values:
* Array of class names that are allowed to be unserialized
* or true if all classes should be allowed (behavior of pre PHP 7.0)
* or false if no classes should be allowed
*
* @var string[]|bool
*/
protected $unserializeClassWhitelist = true;

/**
* @param string[]|bool $unserializeClassWhitelist
*
* @return PhpSerializeOptions
*/
public function setUnserializeClassWhitelist($unserializeClassWhitelist)
{
if (($unserializeClassWhitelist !== true) && (PHP_MAJOR_VERSION < 7)) {
throw new Exception\InvalidArgumentException(
'Class whitelist for unserialize() is only available on PHP 7.0 or higher.'
);
}

$this->unserializeClassWhitelist = $unserializeClassWhitelist;
return $this;
}

/**
* @return string[]|bool
*/
public function getUnserializeClassWhitelist()
{
return $this->unserializeClassWhitelist;
}
}
67 changes: 65 additions & 2 deletions test/Adapter/PhpSerializeTest.php
Expand Up @@ -11,10 +11,11 @@

use PHPUnit\Framework\TestCase;
use Zend\Serializer;
use Zend\Serializer\Exception\InvalidArgumentException;

/**
* @group Zend_Serializer
* @covers Zend\Serializer\Adapter\PhpSerialize
* @group Zend_Serializer
* @covers \Zend\Serializer\Adapter\PhpSerialize
*/
class PhpSerializeTest extends TestCase
{
Expand Down Expand Up @@ -165,4 +166,66 @@ public function testUnserializingInvalidStringRaisesException($string, $expected
$this->expectExceptionMessage($expected);
$this->adapter->unserialize($string);
}

public function testUnserializeNoWhitelistedClasses()
{
$value = 'O:8:"stdClass":0:{}';

if (PHP_MAJOR_VERSION >= 7) {
$this->adapter->getOptions()->setUnserializeClassWhitelist(false);

$data = $this->adapter->unserialize($value);

$this->assertNotInstanceOf(\stdClass::class, $data);
$this->assertInstanceOf('__PHP_Incomplete_Class', $data);
} else {
// In PHP < 7.0 the options-class will throw an exception

self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage('Class whitelist for unserialize() is only available on PHP 7.0 or higher.');

$this->adapter->getOptions()->setUnserializeClassWhitelist(false);
}
}

public function testUnserializeClassNotAllowed()
{
$value = 'O:8:"stdClass":0:{}';

if (PHP_MAJOR_VERSION >= 7) {
$this->adapter->getOptions()->setUnserializeClassWhitelist([\My\Dummy::class]);

$data = $this->adapter->unserialize($value);

$this->assertNotInstanceOf(\stdClass::class, $data);
$this->assertInstanceOf('__PHP_Incomplete_Class', $data);
} else {
// In PHP < 7.0 the options-class will throw an exception

self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage('Class whitelist for unserialize() is only available on PHP 7.0 or higher.');

$this->adapter->getOptions()->setUnserializeClassWhitelist(false);
}
}

public function testUnserializeClassAllowed()
{
$value = 'O:8:"stdClass":0:{}';

if (PHP_MAJOR_VERSION >= 7) {
$this->adapter->getOptions()->setUnserializeClassWhitelist([\stdClass::class]);

$data = $this->adapter->unserialize($value);
$this->assertInstanceOf(\stdClass::class, $data);
$this->assertNotInstanceOf('__PHP_Incomplete_Class', $data);
} else {
// In PHP < 7.0 the options-class will throw an exception

self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage('Class whitelist for unserialize() is only available on PHP 7.0 or higher.');

$this->adapter->getOptions()->setUnserializeClassWhitelist(false);
}
}
}

0 comments on commit 643e8a6

Please sign in to comment.