【问题标题】:Disable echo and print in PHP在 PHP 中禁用回显和打印
【发布时间】:2011-09-27 02:29:18
【问题描述】:

这似乎是一个有趣的问题,但实际上并非如此,我想禁用echoprint 和其他可能输出到缓冲区的函数,例如readfile

之所以要这样做,是为了防止客户端在我的应用程序规则之外使用echoprint,强制他们编译自己的内容并发送到输出类,这样管理整个缓冲区。

现在我知道我可以在脚本开头设置一个输出缓冲区并丢弃任何内容,但这不包括headerset_cookie 之类的东西,所以我的问题可能被解释为 如何控制响应头部的缓冲区

是否有任何可能的方法来管理 PHP 输出的所有方面,例如将回调分配给主缓冲区而不仅仅是响应体?

【问题讨论】:

  • 这几乎就像您想为这些用户页面创建一个代理,但这可能会产生太多开销。在 Java 中,我会有一个过滤器拦截请求并替换响应对象。
  • 它可能可以使用 override_function 来完成,但它需要 PECL apd 扩展。我说可能是因为我不确定 echo 是关键字还是函数。 php.net/manual/en/function.override-function.php
  • @waitinfratrain:这是一个关键字。
  • 哦,好吧,PECL 扩展和输出缓冲的组合就可以完成这项工作。
  • 我认为输出东西的方法太多了,超出你的想象和拦截,所以任何解决方案无论如何都是不完美的解决方案。丢弃输出缓冲区对我来说似乎是最好的策略。

标签: php printing buffer echo


【解决方案1】:

这样做:

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

然后只需调用 trace("your message");随时需要它,并将 $echo 切换为 false 以全局禁用它

【讨论】:

    【解决方案2】:

    您想要做的 - 防止在运行时输出 - 是不可能的。它只是不会发生。但是,如果您执行以下两件事,您可以非常接近:审核可以产生输出的关键字的代码,并在阻止访问输出缓冲区控制功能的同时缓冲输出。

    1. 以编程方式审核代码以确保不存在某些无法捕获的情况(由您决定是否有效地缓存该审核的结果,或者只是在每次页面查看时消耗审核成本 - 不推荐)。

      您可以使用token_get_all() 审核输出关键字,例如T_ECHOT_PRINT (here is a list of all possible tokens)。不要尝试在此处禁用对关键字以外的任何内容的访问,有太多方法可以欺骗(eval()、变量变量、数据://流等)您只是在此处阻止某些关键字。

      不要忘记T_INCLUDET_INCLUDE_ONCET_REQUIRET_REQUIRE_ONCE。这些不仅可以用来包含未经审计的代码(例如 PHP 代码写入临时文件然后包含在内),而且使用一些更高级的文件包装器,它们本身可以用来产生输出。

    2. 使用PHP ADB extension 通过重命名某些方法来禁用对它们的访问。不要试图禁用输出功能,它是行不通的,产生输出的方法太多了。选择像set_cookie()header()这样的特殊的,但是对于实际输出,有无数种方法可以产生输出。阻止这种情况的唯一可靠方法是使用输出缓冲,但禁用对输出缓冲控制方法的访问,这样它们就无法绕过缓冲。

      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]);
              }
          }
      }
      

    请注意,无论您在这方面做得多么出色,几乎肯定会有一种方法可以绕过(例如,他们的代码可能会修补您的应用程序文件的内容以允许再次输出)。 PHP 过于灵活,无法将其牢牢锁定。 PHP 有一个类似的项目,称为安全模式,他们最终放弃了,因为不可能完全安全地锁定所有内容。只要用户有一个完整的执行环境,任何你可以阻止他们的事情,他们都可以撤消。一般而言,在不手动审核每一行的情况下执行用户贡献的代码是非常不明智的(甚至是危险的)。

    【讨论】:

      【解决方案3】:

      除了编辑和重新编译之外,我不相信您可以禁用输出的功能。对于绕过输出缓冲的函数,你的 SOL.

      但是,您可以使用内联输出缓冲来控制非标题输出。最好的部分是它的嵌套功能:

      ob_start();
      echo 'The first output!',"\n";
      
      ob_start();
      echo 'The second output.';
      
      $output2 = ob_get_clean();
      
      ob_flush();
      
      echo $output2;
      

      将输出:

      The first output!
      The second output.
      

      【讨论】:

      • 如果您阅读了我的问题,我确实表示我知道这一点,但这对“set_cookie”等功能没有影响
      • 如果您阅读了我的回答,您对该部分的唯一选择是编辑并重新编译源代码。嵌套缓冲说明了其余部分的可能改进。我会澄清的。
      【解决方案4】:

      最后没有有效的方法来实现这一点,因为至少echo 不是一个函数,而是一个语言结构,不能被禁用。您可以使用输出缓冲(ob_start() 等),但这不会阻止其他代码再次禁用输出缓冲。

      在我看来,除了确保只有“好代码”之外别无他法。我不知道您所说的“防止客户端”是什么意思,但无论如何我都不会执行任意代码。如果它是由纪律严明的开发人员编写并经过测试,那么应该没有问题。

      【讨论】:

      • 当我的应用程序发布时,我无法控制开发人员发布的模块、库等,以迫使他们开发人员不要在网站所有者的上下文中造成错误,我正在研究完全管理输出缓冲区,但似乎没有很好的方法来处理这个问题。
      • @Robert Short 如果您没有发明自己的有限 DSL,您可能无论如何都无法限制开发人员。
      • @RobertPitt:听起来应该有更有效的质量管理。当他们无法阻止自己使用echo 时,即使模块的开发人员指南告诉他们不要使用它,我也不知道他们的模块有多好(不仅仅是echo 的东西) ...一旦模块抛出错误(headers already sent 等),它们就会被视为“错误”。
      • @RobertPitt 我不了解全部情况,但可能会引入自动化模块质量控制系统。他们必须在模块上线之前获得批准。这个自动化测试脚本可以检查脚本中的某些关键字,并告诉他们在将其标记为“启用”之前修复问题,从而在一定程度上确保“好代码”的概念
      猜你喜欢
      • 2011-05-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      相关资源
      最近更新 更多