要求$输入是不安全的.'.PHP'.然后开始上课.如何使其安全,无需使用可以被提及的类的白名单.
例1.(错误代码).
<?php
$input = $_GET['controller'];
require $input . '.php';
new $input;
?>
Run Code Online (Sandbox Code Playgroud)
Ja͢*_*͢ck 13
放弃
我首先应该说,在您的系统中定义静态路由是安全的设计,而这个答案,即使我已经努力减轻安全问题,应该在信任其操作之前进行全面测试和理解.
基础
首先,使用从手册中获取的正则表达式确保控制器包含有效的变量名称; 这清除了明显错误的条目:
$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();
}
Run Code Online (Sandbox Code Playgroud)
执行层次结构
这很好用,但如果有人试图加载内部类呢?它可能会使应用程序失败.
您可以引入一个所有控制器必须扩展或实现的抽象基类或接口:
abstract class Controller {}
// e.g. controller for '?controller=admin'
class Admin extends Controller {}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,为避免名称冲突,您可以在单独的命名空间中定义它们.
这就是你如何强制执行这样的层次结构:
if ($controller !== null) {
// load and use controller
require_once("$controller.php");
if (is_subclass_of($controller, 'Controller')) {
$c = new $controller();
}
}
Run Code Online (Sandbox Code Playgroud)
我is_subclass_of()在实例化类之前用来键入check.
自动加载
而不是使用的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
}
});
Run Code Online (Sandbox Code Playgroud)
这也是您可以规范化类名称的地方,以便它更好地映射到文件名,以及强制执行自定义命名空间,例如"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();
}
}
Run Code Online (Sandbox Code Playgroud)
所有这些代码都假定您有适当的错误处理代码; 对于实施建议,您可以查看此答案.