根据用户输入创建类
2022-08-30 21:16:03
要求$input是不安全的。“.php”。然后插入一个类。我如何使它安全,而不需要使用可以识别的类的白名单。
例 1.(代码错误)。
<?php
$input = $_GET['controller'];
require $input . '.php';
new $input;
?>
要求$input是不安全的。“.php”。然后插入一个类。我如何使它安全,而不需要使用可以识别的类的白名单。
例 1.(代码错误)。
<?php
$input = $_GET['controller'];
require $input . '.php';
new $input;
?>
免責聲明
我应该首先说,在系统中定义静态路由是安全的,而这个答案,即使我已经努力缓解安全问题,也应该在信任其操作之前进行彻底的测试和理解。
基础知识
首先,确保控制器包含有效的变量名称,使用从手册中获取的正则表达式;这清除了明显的错误条目:
$controller = filter_input(INPUT_GET, FILTER_VALIDATE_REGEXP, [
'options' => [
'regexp' => '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',
'flags' => FILTER_NULL_ON_FAILURE,
]
]);
if ($controller !== null) {
// load and use controller
require_once("$controller.php");
$c = new $controller();
}
强制实施层次结构
这很有效,但是如果有人尝试加载内部类怎么办?它可能会使应用程序失败。
您可以引入一个抽象基类或接口,所有控制器都必须扩展或实现它:
abstract class Controller {}
// e.g. controller for '?controller=admin'
class Admin extends Controller {}
顺便说一句,为了避免名称冲突,您可以在单独的命名空间中定义它们。
这就是你如何强制实施这样的层次结构:
if ($controller !== null) {
// load and use controller
require_once("$controller.php");
if (is_subclass_of($controller, 'Controller')) {
$c = new $controller();
}
}
我在实例化类之前使用is_subclass_of()
进行类型检查。
自动加载
在这种情况下,您可以使用自动加载程序来代替使用 :require_once()
// register our custom auto loader
spl_autoload_register(function($class) {
$file = "$class.php"; // admin -> admin.class.php
if (file_exists($file)) {
require_once $file; // this can be changed
}
});
这也是您可以规范化类名的地方,以便它更好地映射到文件名,以及强制执行自定义命名空间,例如“App\\$class.php”。
这将代码减少一行,但使加载更加灵活:
if ($controller !== null) {
// check hierarchy (this will attempt auto loading)
if (class_exists($controller) && is_subclass_of($controller, 'Controller')) {
$c = new $controller();
}
}
所有这些代码都假设您有正确的错误处理代码;有关实现建议,您可以查看此答案。
一些建议:
使您的过滤器尽可能严格,例如。
/* is $_GET['controller'] set? */
if (!isset($_GET['controller'])) {
// load error or default controller???
}
$loadController = $_GET['controller'];
/* replace any characters NOT matching a-z or _ (whitelist approach), case insensitive */
$loadController = preg_replace('/[^a-z_]+/i', '', $loadController);
/* verify var is not empty now :) */
if (!$loadController) {
// load error or default controller???
}
/* if your classes are named in a certain fashion, eg. "Classname", format the incoming text to match ** NEVER TRUST USER INPUT ** */
$loadController = ucfirst(strtolower($loadController));
检查文件是否存在 为什么不file_exists?
/* avoiding using file_exists as it also matches folders... */
if (!is_file($myControllerClassesPath.$loadController.'.php')) {
// load error or default controller???
}
然后需要该文件,并验证该类本身是否存在
require($myControllerClassesPath.$loadController.'.php');
/* of course, this assumes filename === classname, adjust accordingly */
if (!class_exists($loadController)) {
// load error or default controller???
}
然后,当然,X的新实例
new $loadController;