19

$input を要求するのは安全ではありません。「.php」。その後、クラスを開始します。開始できるクラスのホワイトリストを使用する必要なく、どうすれば安全にできますか。

例 1. (悪いコード)。

<?php

$input = $_GET['controller'];

require $input . '.php';

new $input;

?>
4

9 に答える 9

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();
}

階層の強制

これはうまく機能しますが、誰かが代わりに内部クラスをロードしようとした場合はどうなるでしょうか? アプリケーションがひどく失敗する可能性があります。

すべてのコントローラーが拡張または実装する必要がある抽象基本クラスまたはインターフェイスを導入できます。

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()この場合、a を使用する代わりに、代わりにオートローダーを使用できます。

// 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"

これにより、コードが 1 行削減されますが、読み込みがより柔軟になります。

if ($controller !== null) {
    // check hierarchy (this will attempt auto loading)
    if (class_exists($controller) && is_subclass_of($controller, 'Controller')) {
        $c = new $controller();
    }
}

このコードはすべて、適切なエラー処理コードが配置されていることを前提としています。実装の提案については、この回答を見ることができます。

于 2013-04-22T03:15:31.627 に答える
0

最初にこの関数を追加します。

function __autoload ( $class ) {
     $path = "../path/to/class/dir/" . $class . TOKEN . ".php";
     if ( file_exists ($path) ) {
          require_once ( $path );
     } else {
          // class not found.
     }
}

次に、クラスにアクセスするだけで、

$class = new input();

ファイル"../path/to/class/dir/input_secretToken.php"が存在するかどうかを確認し、自動的に含めます。

TOKENこれは、構成ファイルで定義され、すべてのクラス ファイルのサフィックスとして使用される合言葉です。そのため、トークン サフィックスを持つクラス ファイルのみがロードされます。

于 2013-04-22T03:29:44.347 に答える
0

の使用を検討してくださいspl_autoload_register()。これにより、ファイル/クラスなどの検証に多くの労力を節約できます.

<?php
function autoloadClasses($class) {
    if (file_exists('core/'.$class.'.php')) {
        include 'core/'.$class . '.php';
    }
}
spl_autoload_register('autoloadClasses');

?>

次にdart.php、core-folder にファイル名を保存します (ファイル名とクラス名は同じである必要があります)。

次にオブジェクトを作成する new dart();と、必要に応じてファイルが含まれます。

これに関する詳細情報: http://php.net/manual/en/function.spl-autoload-register.php

于 2013-04-19T10:32:00.390 に答える
0

クラス/ファイルが少ない場合は、クラスが保存されているフォルダー内のすべての php ファイルを取得し、含める/必要とするクラスがそれらの 1 つであるかどうかを確認できます。

だから、このようなもの:

$classDir = '/path/to/classes';
$classList = glob($classDir.'/*.php');
$classAbsolutePath = $classDir.'/'.$_GET['class'];

if (in_array($classAbsolutePath, $classList)) {
    require $classAbsolutePath;
}

サブディレクトリがある場合は、それに応じてこのコードを変更する必要があります。ところで、パフォーマンスに関しては、これは最適な解決策ではありません。多くのファイルと多くのサブディレクトリがある場合は特にそうです。また、in_array()あまり効率的ではないため、大きな配列がある場合は避ける必要があります。

私の意見では、このようなことを行う最善の方法は、ホワイトリストを作成することです。コードを介して自動的に生成できます。プロジェクトを再構築または展開するたびに、リストを再生成して、常に有効なリストを保持できます。

于 2013-04-21T12:23:13.263 に答える
0

許可されたファイルに特別なタグを導入することをお勧めします。次に、ファイルを含める前に、プレーン テキストとして読み取り、タグを探します。タグが存在する場合にのみ含めます。タグは、許可されたファイルの先頭にある PHP コメント内に含めることができます。

$class = $_GET['class'];
if (preg_match('/^[a-zA-Z]+$/', $class))
{
    $file = $class.".php";
    if (is_file($file)) {
    {
        $content = file_get_contents($file);
        if (strpos($content, "THECLASSMAGIC") !== false)
        {
            require($file);
        }
    }
    else
    {
        die(...);
    }
}
else
{
    die(...);
}
于 2013-04-21T12:24:46.707 に答える
-1

どのような場合に、ユーザーがクエリ文字列パラメーターを介してコントローラーをインスタンス化できるようにしますが、ユーザーが実際にインスタンス化しようとしていることを理解していませんか? 災害のレシピのように聞こえます。

そうは言っても、入力を文字のみに制限し(クラスの名前がMyClass.phpMyOtherClass.phpなどであると仮定)、特定のディレクトリにロックします。

<?php

$className = $_GET['file'];
$dir = '/path/to/classes/';
$file = $dir . $className . '.php';

if (preg_match('/^[a-zA-Z]+$/', $className) && is_file($file)) {
    require($file);
    $class = new $className;
}
else {
    die('Class not found');
}
于 2013-04-21T12:08:23.407 に答える