11

ディレクトリ トラバーサル攻撃を防ぐために、外部リソースからのパスを正規化したいと考えています。realpath()関数については知っていますが、残念ながらこの関数は既存のディレクトリのパスしか返しません。そのため、ディレクトリが (まだ) 存在しない場合、realpath() 関数はパスの存在しない部分全体を切り捨てます。

だから私の質問は:パスを正規化するだけのPHP関数を知っていますか?

PS: また、すべての可能なディレクトリを事前に作成したくありません;-)

4

5 に答える 5

6

このための組み込みの PHP 関数はありません。代わりに次のようなものを使用してください。

function removeDots($path) {
    $root = ($path[0] === '/') ? '/' : '';

    $segments = explode('/', trim($path, '/'));
    $ret = array();
    foreach($segments as $segment){
        if (($segment == '.') || strlen($segment) === 0) {
            continue;
        }
        if ($segment == '..') {
            array_pop($ret);
        } else {
            array_push($ret, $segment);
        }
    }
    return $root . implode('/', $ret);
}
于 2012-04-09T01:24:36.080 に答える
2

Tamas のソリューションは機能すると思いますが、正規表現を使用することも可能です。ヴァルの解決策は正しくありません。しかし、これは機能します。

function normalizePath($path) {
    do {
        $path = preg_replace(
            array('#//|/\./#', '#/([^/.]+)/\.\./#'),
            '/', $path, -1, $count
        );
    } while($count > 0);
    return $path;
}

はい、可能性のある ./\ などのすべての可能な異なるエンコーディングを処理するわけではありませんが、それはその目的ではありません。1 つの関数は 1 つのことだけを行う必要があるため、 にも変換する場合は%2e%2e%2f../最初に別の関数で実行します。

Realpath はシンボリック リンクも解決しますが、パスが存在しない場合、これは明らかに不可能です。ただし、余分な「/./」、「/../」、および「/」文字を取り除くことができます。

于 2013-08-20T14:44:53.037 に答える
2

Benubird / Cragmonkey のおかげで、状況によっては以前の回答が機能しなかったことが修正されました。したがって、元の目的のために新しいものを作成します。より少ない行で、純粋な正規表現を使用して実行します。

今回は、以下のようなより厳密なテストケースでテストしました。

$path = '/var/.////./user/./././..//.//../////../././.././test/////';

function normalizePath($path) {
    $patterns = array('~/{2,}~', '~/(\./)+~', '~([^/\.]+/(?R)*\.{2,}/)~', '~\.\./~');
    $replacements = array('/', '/', '', '');
    return preg_replace($patterns, $replacements, $path);
}

正解は /test/ です。

競争を意図したものではありませんが、パフォーマンス テストは必須です。

テスト ケース: Windows 7、i5-3470 クアッド コア、3.20 GHz で 10 万回ループ。

私の:1.746秒。

トム イムレイ: 4.548 秒。

ベヌバード: 3.593 秒。

おおぐま座: 4.334 秒。

私のバージョンが常に優れているという意味ではありません。いくつかの状況で、それらは同じように機能します。

于 2013-01-16T09:24:49.917 に答える