|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2010-01-14 16:52 UTC] andrew at trib dot tv
Description:
------------
My case is one in which var_export is being used to expose private members of classes present in the context of an error. So an error occurs, a custom handler is invoked, and it var_exports any variables in the error context which are objects, so that developers investigating the problem can see the values of private vars within these objects at the time the error occured. However, doing this:
$str = var_export($object, true);
If $object contains recursive references, var_export not only fails with a fatal error that cannot be handled or suppressed, but also flushes its internal output buffer, so if you've set the second argument to true, wanting the output returned rather than sent to the browser, you're out of luck, because it's gone.
This is a security issue, because in some situations, a developer may have implemented var_export on objects with passwords stored within, and expects to get the object returned in a string. If the entire content of the object (up to the recursive reference) is instead output to the browser, this data is inadvertently exposed.
I'd say that in such situations var_export should trigger an E_WARNING and return null or false. Then it can be safely used on objects that do not contain recursive references without having to know whether they do or not before you call the function.
I'm aware of previous bugs filed on this issue, notably 17874 and 16074, so I raise this specifically in relation to te security implications, and also making the point that it is not possible to detect whether an object contains *private* recursive references before you take your life in your hands by throwing it at var_export and crossing your fingers!
Cheers,
Andrew
Reproduce code:
---------------
<?php
class PrivateThing {
public $publicvar = 'notsecret';
private $password = 'supersecret';
private $reftoself;
function __construct() { $this->reftoself = $this; }
}
$x = new PrivateThing;
$y = @var_export($x, true);
echo "No passwords here!";
?>
Expected result:
----------------
No passwords here!
Actual result:
--------------
PrivateThing::__set_state(array(
'publicvar' => 'notsecret',
'password' => 'supersecret',
'reftoself' =>
PrivateThing::__set_state(array(
'publicvar' => 'notsecret',
'password' => 'supersecret',
'reftoself' =>
PrivateThing::__set_state(array(
'publicvar' => 'notsecret',
'password' => 'supersecret',
'reftoself' =>
PrivateThing::__set_state(array(
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Nov 01 22:00:02 2025 UTC |
Incidentally, I'm aware that removing the @ reveals a fatal error - in fact I said so in my OP. For the sake of absolute clarity, this is what you get without the @ (and with display_errors enabled): PrivateThing::__set_state(array( 'publicvar' => 'notsecret', 'password' => 'supersecret', 'reftoself' => PrivateThing::__set_state(array( 'publicvar' => 'notsecret', 'password' => 'supersecret', 'reftoself' => PrivateThing::__set_state(array( 'publicvar' => 'notsecret', 'password' => 'supersecret', 'reftoself' => PrivateThing::__set_state(array( Fatal error: Nesting level too deep - recursive dependency? in /root/test.php on line 9 So you still get the spurious output, prior to the fatal error. I'm not objecting to a recursive input triggering an error, but it should ideally not be fatal, and most importantly it surely shouldn't cause a half-complete serialisation to be sent to the browser when you don't expect this function to produce any output at all.