web-dev-qa-db-ja.com

PHPでのディレクトリトラバーサルの防止、ただしパスの許可

私はベースパス/ whatever/foo /を持っています

および$_GET['path']は、それに対して相対的でなければなりません。

しかし、ディレクトリトラバーサルを許可せずに、これをどのようにして行う(ディレクトリを読み取る)のですか

例えば。

/\.\.|\.\./

正しくフィルタリングされません。

34
Johnny

まあ、1つのオプションは、実際のパスを比較することです。

_$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);

$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);

if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
    //Directory Traversal!
} else {
    //Good path!
}
_

基本的に、 realpath() は、提供されたパスを実際のハード物理パスに解決します(シンボリックリンクの解決、_.._、_._、_/_、_//_など)...したがって、実際のユーザーパスが実際のベースパスで始まっていない場合は、トラバーサルを実行しようとしています。 realpathの出力には、_._や_.._などの「仮想ディレクトリ」はありませんないことに注意してください ...

111
ircmaxell

ircmaxellの答えは完全に正しくありませんでした。私はいくつかのスニペットでその解決策を見てきましたが、realpath()の出力に関連するバグがあります。 realpath()関数は、末尾のディレクトリ区切り文字を削除するため、次のような2つの連続するディレクトリを想像してください。

_/foo/bar/baz/

/foo/bar/baz_baz/
_

realpath()は最後のディレクトリ区切り文字を削除するため、_$_GET['path']_が "../baz_baz"と等しい場合、メソッドは "適切なパス"を返します。

_strpos("/foo/bar/baz_baz", "/foo/bar/baz")
_

多分:

_$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);

$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);

if ($realUserPath === false || strcmp($realUserPath, $realBase) !== 0 || strpos($realUserPath, $realBase . DIRECTORY_SEPARATOR) !== 0) {
    //Directory Traversal!
} else {
    //Good path!
}
_
16

../などのパターンをチェックするだけでは不十分です。たとえば、URIが "%2e%2e%2f"にエンコードする "../"を使用します。パターンチェックがデコードの前に行われると、このトラバーサルの試みを見逃してしまいます。特にエンコードされた文字列を使用する場合に、ハッカーがパターンチェッカーを回避するために実行できる他のいくつかのトリックがあります。

私は、ircmaxwellが示唆するように、realpath()のようなものを使用して、任意のパス文字列をその絶対パスに正規化することで、これらを阻止する最も成功しています。その後、トラバーサル攻撃のチェックを、事前に定義したベースパスと照合することで始めます。

5
Cowlby

正規表現を使用してすべての../sを削除しようとする誘惑に駆られるかもしれませんが、PHPに組み込まれた素敵な関数がいくつかあります。

$page = basename(realpath($_GET));

basename-パスからすべてのディレクトリ情報を取り除きます。 ../pages/about.phpabout.phpになります

realpath-ファイルへのフルパスを返します。 about.php/home/www/pages/about.phpになりますが、ファイルが存在する場合のみです。

結合すると、ファイル名のみが返されますが、ファイルが存在する場合のみです。

1
L4m0r

1

-Indexブロックにnullのindex.htmを配置する

2

開始時にsQSをフィルタリングする

// Path Traversal Attack
if( strpos($_SERVER["QUERY_STRING"], "../") ){
    exit("P.T.A. B-(");
}
0
Lo Vega

新しいファイルまたはフォルダーの作成を調べると、2段階のアプローチを使用できることがわかりました。

最初に、realpath()のような関数のカスタム実装を使用して走査試行をチェックします。ただし、これは既存のファイルだけでなく、任意のパスに対して機能します。良い出発点があります ここurldecode()と、確認する価値があると思われるその他の機能で拡張します。

この粗雑な方法を使用すると、トラバーサルの試みを除外できますが、特殊文字、シンボリックリンク、エスケープシーケンスなどのハックな組み合わせを見逃す可能性があります。ただし、ターゲットファイルが存在しないことを確認しているため(file_existsを使用して確認) )誰も何も上書きできません。最悪のケースのシナリオは、誰かがあなたのコードがどこかにファイルまたはフォルダーを作成することを可能にすることです。あなたのコードがそのファイル/フォルダーにすぐに書き込むことを許可しない限り、これはほとんどの場合許容できるリスクです。

最後に、パスが既存の場所を指すようになりました。したがって、上記で提案したrealpath()を使用したメソッドを使用して適切なチェックを実行できます。この時点でトラバーサルが発生したことが判明した場合でも、ターゲットパスへの書き込みの試行を確実に防止する限り、多かれ少なかれ安全です。また、現在、ターゲットファイル/ディレクトリを削除して、トラバーサルの試みだったと言うことができます。

ハッキングできないと言っているわけではありません。FSに対して違法な変更を加えることは可能ですが、カスタムチェックだけを行うよりも、realpath()を利用できないため、ウィンドウが一時的で空のファイルまたはフォルダーをどこかに作成することで開いたままにした虐待は、一部のEdgeケースを見落とす可能性のあるカスタムチェックでのみ発生するため、永続化して書き込むこともできます。

また、plsが間違っている場合は修正してください。

0
Grapestain

sersがディレクトリをトラバースすることを許可せずにそうだと思いますか?

自分のPHPがディレクトリをトラバースしないようにする場合は、まずphpを適切に機能させる必要があります。

ユーザーを停止するために必要なのは、変更された.htaccessファイルです...

Options -Indexes

(これはすべて、ユーザーについて話していることを前提としています)

0
J V