开发者

Disable echo and print in PHP

This may seem like a funny question but in fact it's not, I would like to disable echo, print and other functions that may output to the buffer such as readfile.

开发者_开发百科

The reason why I would like to do this is to prevent the client from using echo or print outside the rules of my application, forcing them to compile their contents and send it to the output class, so that the whole buffer is managed.

Now I know I can set up an output buffer at the start of my script and throw away any content, but this will not include things such as header and set_cookie, so my question may be interpreted as How can I control the buffer for the head of the response

Is there any possible way to manage all aspects of PHP's outputting, such as assign a callback to the main buffer rather then just the body of response?


At the end there is no effective way to achieve this, because at least echo is not a function, but a language construct, that cannot get disabled. You may play around with output buffering (ob_start() and such), but that will not prevent other code to disable the output buffering again.

In my eyes there is no way around but to make sure, there is only "good code". I don't know, what you mean by "prevent the client", but I would not execute arbitrary code anyway. And if its written by disciplined developers and its tested, there should be no problem then.


Other than editing and recompiling, I don't believe you can disable functions that output. For functions that bypass output buffering, your SOL.

You can, however, use inline output buffering to control non-header output. The best part is it's nesting capability:

ob_start();
echo 'The first output!',"\n";

ob_start();
echo 'The second output.';

$output2 = ob_get_clean();

ob_flush();

echo $output2;

will output:

The first output!
The second output.


What you want to do - prevent outputting at runtime - isn't possible. It just isn't going to happen. You can get very close if you do these two things though: audit the code for keywords that can produce output, and buffer output while preventing access to the output buffer control functions.

  1. Audit the code programmatically to make sure certain untrappable situations don't exist (it's up to you to cache the result of that audit efficiently, or just eat the cost of auditing on every page view - not recommended).

    You can use token_get_all() to audit for output keywords such as T_ECHO and T_PRINT (here is a list of all possible tokens). Don't try to disable access to anything but keywords here, there's too many ways to trick that (eval(), variable variables, data:// streams, etc.) You're only blocking certain keywords here.

    Don't forget T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, and T_REQUIRE_ONCE. Not only could these be used to include unaudited code (such as PHP code written to a temp file then included), but using some of the more advanced file wrappers, these themselves can be used to produce output.

  2. Use the PHP ADB extension to disable access to certain methods by renaming them. Don't try to disable output functions, it's just not going to work, there's too many ways to generate output. Pick out the special ones like set_cookie() and header(), but for actual output, there's innumerable ways to produce output. The only surefire way to block this is to use output buffering, but disable access to the output buffer control methods so they have no way to bypass buffering.

    class YourApplicationControllingClass {
    
        final protected function callUserCode($pathToUserCodeFile) {
            // We want this to be a local variable so there's no way to get to it
            // with PHP Reflection
            $suspender = new SuspendFunctions();
    
            ob_start();
    
            // You may need to add more here, this is just my superficial pass
            $suspender->suspend("ob_clean", "ob_end_clean", "ob_end_flush", "ob_flush", 
                    "ob_get_clean", "ob_get_contents", "ob_get_flush", "ob_get_length",
                    "ob_get_level", "ob_get_status", "ob_implicit_flush", "ob_list_handlers",
                    "ob_start", "output_add_rewrite_var", "output_reset_rewrite_vars",
                    "set_cookie", "set_raw_cookie", "header_register_callback", "header",
                    "header_remove", "http_response_code", "register_shutdown_function",
                    "register_tick_function", "unregister_tick_function", "set_error_handler",
                    "restore_error_handler", "set_exception_handler", "restore_exception_handler"
                    );
    
            $this->callUserCodeSandbox($pathToUserCodeFile);
            // Restore our original execution environment
            $suspender->resume();
    
            $content = ob_get_clean();
            // If you want to be aggressive, check to see if they produced any output
            // and blacklist them if they even try.
            if ($content !== '') $this->blacklistUserCode($pathToUserCodeFile);
        }
    
        private function callUserCodeSandbox($pathToUserCodeFile) {
            require($pathToUserCodeFile);
        }
    }
    
    final class SuspendFunctions {
        private $suspendedFunctions = array();
    
        /**
        * Suspends certain functions from being executable.
        * @param string $function,... Names of functions to suspend, you may pass multiple
        * @return void
        */
        function suspend($function) {
            $functions = func_get_args();
            foreach($functions as $function) {
                // Make sure we don't double-suspend a function
                if (isset($this->suspendedFunctions[$function])) continue;
    
                // Make new names unguessable, and make it very unlikely that we end up with a collision.
                $newName = '_'.md5($function.microtime(true).mt_random());
    
                // Rename to the unguessable name
                rename_function($function, $newName);
    
                // Keep a record for ourselves what this new name is so we can resume later
                $this->suspendedFunctions[$function] = $newName;
            }
        }
    
        /**
        * Resumes functions for calling
        */
        function resume() {
            foreach($this->suspendedFunctions as $function=>$newName) {
                rename($newName, $function);
                unset($this->suspendedFunctions[$function]);
            }
        }
    }
    

Just be aware, no matter how good you are at this, there's almost certainly going to be a way to bypass (for example, maybe their code patches the content of one of your application's files to permit output again). PHP is too flexible to lock this down solidly. PHP had a similar project called Safe Mode, which they eventually abandoned because it was impossible to totally securely lock everything down. As long as the user has a full execution environment, anything you can do to block them, they can undo. Very unwise in general to execute user contributed code without hand-auditing each and every line (and even dangerous then).


do it this way :

    function trace($message) {
      $echo = true;
      if ($echo)
      echo $message . "<br>";
    }

then just call trace("your message"); anytime you need it, and switch $echo to false to disable it globally

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜