这似乎是一个有趣的问题,但实际上并非如此,我想禁用echo
,print
以及可能输出到缓冲区的其他函数,例如readfile
。< / p>
我之所以这样做是为了防止客户端在我的应用程序规则之外使用echo
或print
,强制他们编译内容并将其发送到输出类,以便管理整个缓冲区。
现在我知道我可以在脚本开头设置输出缓冲区并丢弃任何内容,但这不包括header
和set_cookie
等内容,所以我的问题可能是解释为如何控制响应头部的缓冲区
有没有办法管理PHP输出的所有方面,例如将回调分配给主缓冲区而不仅仅是响应主体?
答案 0 :(得分:6)
答案 1 :(得分: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.
答案 2 :(得分:1)
您想要做的事情 - 阻止在运行时输出 - 是不可能的。它不会发生。如果你做这两件事,你可以非常接近:审核可以产生输出的关键字的代码,缓冲输出,同时阻止访问输出缓冲控制函数。
以编程方式审核代码,以确保不存在某些无法解决的情况(由您自行缓存该审核的结果,或者只是在每个页面视图上花费审核成本 - 不推荐)。
您可以使用token_get_all()
审核输出关键字,例如T_ECHO
和T_PRINT
(here is a list of all possible tokens)。不要尝试禁止访问除了关键字之外的任何内容,有太多方法可以欺骗(eval()
,变量变量,数据://流等)。您只是在这里阻止某些关键字。
不要忘记T_INCLUDE
,T_INCLUDE_ONCE
,T_REQUIRE
和T_REQUIRE_ONCE
。这些不仅可以用于包括未经审计的代码(例如写入包含在临时文件中的PHP代码),而是使用一些更高级的文件包装器,这些包装器本身可用于生成输出。
使用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 :(得分:1)
这样做:
function trace($message) {
$echo = true;
if ($echo)
echo $message . "<br>";
}
然后只是跟踪(“你的消息”);任何时候你需要它,并将$ echo切换为false以全局禁用它