Аудитпрограммируйте программно, чтобы убедиться, что определенные ситуации, которые невозможно перехватить, не существуют (это зависит от вас, чтобы эффективно кэшировать результаты этого аудита или просто съесть затраты на аудит на каждом просмотре страницы - не рекомендуется).
Вы можете использовать token_get_all()
для аудита выходных ключевых слов, таких как T_ECHO
и T_PRINT
( - список всех возможных токенов ).Не пытайтесь отключить доступ к чему-либо, кроме ключевых слов, здесь слишком много способов обмануть это (eval()
, переменные переменные, data: // streams и т. Д.). Здесь вы блокируете только определенные ключевые слова.
Не забудьте T_INCLUDE
, T_INCLUDE_ONCE
, T_REQUIRE
и T_REQUIRE_ONCE
.Они могут быть использованы не только для включения неаудированного кода (например, PHP-кода, записанного во временный файл, а затем включены в него), но и с помощью некоторых из более продвинутых оболочек файлов, сами они могут использоваться для вывода.
Используйте расширение PHP ADB , чтобы отключить доступ к определенным методам, переименовав их.Не пытайтесь отключить функции вывода, это просто не сработает, есть слишком много способов для генерации вывода.Выберите специальные, такие как 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]);
}
}
}