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