
インジェクション-パストラバーサル
パストラバーサルは、インジェクションの脆弱性としてよく見られるもう 1 つのタイプです。URI の構築 (URL、ファイルパスなど) で、完全に解決されたパスがルートの外側を指していないことが適切に保証されない場合に発生しがちです。 意図した 道。
パストラバーサルは実際にはパス*インジェクション*の脆弱性であると見なされることもあることを強調しておく必要があります。
パストラバーサル脆弱性の影響は、トラバーサルが発生する状況と、実施された全体的な強化方法に大きく依存します。しかし、その前に、この脆弱性の簡単な実際的な例を見て、私たちが何を話しているのかを確認しておきましょう。
簡単な内訳
契約書や求人のテンプレートなど、ドキュメントを提供するアプリケーションのエンドポイントを考えてみましょう。これらはすべて PDF のように、アプリケーション内では静的なファイルである可能性があります。
このような状況では、リクエストに応じてファイルを取得するための次のようなコードがあるかもしれません。
baseFolder =「/var/www/api/documents/」とします。
パス = ベースフォルダ + request.params.filename; とします。
ファイル.read (パス) を返します。
脆弱性がどのように発生するかを示すには、アプリケーションのルートがどこにあるかも知っておく必要があります。この例では、アプリケーションのルートが「/var/www/api/' にあると仮定します。
アプリケーションが 'filename' パラメーターを取ることはわかっています。入力の例をいくつか見て、その結果を見てみましょう。
'.. /' を使用してファイルシステムをトラバースできることに注目してください。PDF が通常保存されている「documents」フォルダから、Linux ではパスワードハッシュを含む「shadow」ファイルを含む「/etc/」フォルダに移動できます。ご想像のとおり、これは実際には理想的ではありません。
URL のトラバーサルを見る
パストラバーサルの別の変形は、API とのやりとりを目的とした URL を作成する場合に発生する可能性があります。以下のメソッドを持つ API があるとします。
このAPIは、たとえば注文に関する情報を取得しようとしたときに、APIを呼び出す可能性のある別のアプリケーションによって操作されます。
APIBase = "https://my.api/api/v1「;
orderAPI = apiBase +「/order/get」としましょう。
let apiURL = orderAPI + request.params.orderId;
レスポンス = http.get (APIURL); とします。
ユーザーから提供された注文 ID によっては、どうなるのでしょうか?以下に、入力された内容に基づいて呼び出される有効な URL を示します。
正規化は通常、クライアント側では行われませんが (可能ですが)、ウェブサーバーはリクエストを以下の形式に正規化します。
2 番目の例の入力では、ID 番号 '1 の注文を取得するのではなく、実際に delete メソッドを呼び出しています。その結果、もちろん注文が削除されます。
緩和策
パストラバーサルについて議論する場合、直接的な緩和策と、できるだけ頻繁に適用できる間接/防御手法の両方があります。まず、パスの処理方法を見てみましょう。
直接的な緩和
パスの処理に関しては、パス解決、つまりパス正規化のプロセスとその重要性を理解する必要があります。
'/var/www/api/documents/../../../../.. のようなパスがある場合/etc/shadow 'は非正規のパスにあります。ファイルシステムからこのパスを要求すると、'/etc/shadow' に正規化されます。カノニカルでないパスを開こうとしないことが重要です。むしろ、まずパスを正規化し、目的のファイルまたはフォルダのみを指していることを確認してから読み込むべきです。
baseFolder =「/var/www/api/documents/」とします。
パス = ベースフォルダ + request.params.filename; とします。
ResolvedPath = path.resolve (パス); とします。
もし (!解決済みパス。(ベースフォルダ)) で始まる
「ベースフォルダの外側を読み込もうとしました」を返します。
他に
ファイル.read (解決されたパス) を返します。
アンチパターン-ファイル名をサニタイズしようとしています
次のようなことをしたくなるかもしれません。
baseFolder =「/var/www/api/documents/」とします。
let path = baseFolder + request.params.filename.replace (「../」,「」);
...
ただし、このアプローチでは じゃない 使用してください。パスを扱う際の鍵は、常に標準パスを確認することです。
カノニカルパスがルールを破っていない限り、パスが最終的にどのように構築されるかは、実際には何の違いもありません。このようなパスをサニタイズしようとすると、エラーが非常に発生しやすく、安全性が確保されることはほとんどありません。
アクセスを制限する
前の例では、Linuxではパスワードハッシュを含むファイルである「/etc/shadow」ファイルの読み取りを使用しました。しかし、アプリケーションがそのファイルやルート以外のファイルを読み込めるようにすべき理由はまったくありません。
コンテナを採用すれば、すでに多くのリスクを軽減できているでしょう。コンテナーを強化する (root として実行するなどしない) ための対策を講じることは極めて重要です。Web プロセスからすべての権限を削除し、ファイルシステムでの読み取り権限を厳密に必要なファイルだけに制限することを強くお勧めします。
例
次に、さまざまな言語でいくつかの例を紹介して、実際の動作をもう少しわかりやすく説明します。
C#-安全でない
フルパスを解決しないか、パスのファイル名部分のみを使用するようにすると、コードがパストラバーサルの影響を受けやすくなります。
var BaseFolder =「/var/www/app/documents/」;
var ファイル名 =「../../../../../etc/passwd「;
//安全でない:/etc/passwd を読み込む
var FileContents = File.readAllText (Path.Combine (ベースフォルダ、ファイル名));
C#-セキュア-カノニカル
この例では、フル (絶対) パスを解決し、ファイル解決パスがベースフォルダ内にあることを確認することで、パストラバーサルを防止しています。
var BaseFolder =「/var/www/app/documents/」;
var ファイル名 =「../../../../../etc/passwd「;
var CanonicalPath = path.getFullPath (Path.Combine (ベースフォルダ、ファイル名));
//SECURE: 指定されたベース以外での読み取りの試みを拒否します。
もし (!正規パス。(基本フォルダー) で始まる)
「ベースフォルダ外のファイルを読み込もうとしています」を返します。
var FileContents = File.ReadAllText (正規パス);
C#-セキュア-ファイル名
この例では、パスのファイル名部分のみを取得して、指定されたフォルダーからトラバースできないようにすることで、パストラバーサルを防止しています。
var BaseFolder =「/var/www/app/documents/」;
//他のサブフォルダに移動できない場合のみ使用してください
var ファイル名 = path.getFileName (「../../../../../../etc/passwd「);
//セキュア:/var/www/app/documents/passwd を読み取ります
var FileContents = File.readAllText (Path.Combine (ベースフォルダ、ファイル名));
Java-安全ではありません
フルパスを解決しないか、パスのファイル名部分のみを使用するようにすると、コードがパストラバーサルの影響を受けやすくなります。
文字列ベースフォルダ =「/var/www/app/documents/」;
文字列ファイル名 =「../../../../../etc/passwd「;
//安全でない:/etc/passwd を読み込む
パスファイルパス = パス.GET (ベースフォルダ+ファイル名);
<String>行を一覧表示 = ファイル。すべての行を読み込む (ファイルパス);
Java-セキュア-カノニカル
この例では、フル (絶対) パスを解決し、ファイル解決パスがベースフォルダ内にあることを確認することで、パストラバーサルを防止しています。
文字列ベースフォルダ =「/var/www/app/documents/」;
文字列ファイル名 =「../../../../../etc/passwd「;
//安全でない:/etc/passwd を読み込む
パス正規化パス = Paths.get (ベースフォルダ+ ファイル名) .normalize ();
もし (!正規化されたパス.toString (). (ベースフォルダ)) で始まる
{
「ルート外のパスを読み込もうとしています」を返します。
}
他に
{
<String>行を一覧表示 = ファイル。すべての行を読み込む (正規化されたパス);
}
Java-セキュア-ファイル名
この例では、パスのファイル名部分のみを取得して、指定されたフォルダーからトラバースできないようにすることで、パストラバーサルを防止しています。
文字列ベースフォルダ =「/var/www/app/documents/」;
//他のサブフォルダに移動できない場合のみ使用してください
文字列ファイル名 = パス.GET (「../../../../../../etc/passwd「) .getFileName () .toString ();
//セキュア:/var/www/app/documents/passwd を読み取ります
パスファイルパス = パス.GET (ベースフォルダ+ファイル名);
<String>行を一覧表示 = ファイル。すべての行を読み込む (ファイルパス);
ジャバスクリプト-安全ではありません
フルパスを解決しないか、パスのファイル名部分のみを使用するようにすると、コードがパストラバーサルの影響を受けやすくなります。
定数fs = 必須 ('fs');
const BaseFolder =「/var/www/app/documents/」;
const ファイル名=「../../../../../../etc/passwd「;
//安全でない:/etc/passwd を読み込む
const data = fs.readFileSync (BaseFolder + FileName、'utf8');
ジャバスクリプト-セキュア-カノニカル
この例では、フル (絶対) パスを解決し、ファイル解決パスがベースフォルダ内にあることを確認することで、パストラバーサルを防止しています。
定数 fs = 必須 (「fs」);
定数パス = 必須 (「パス」);
const BaseFolder =「/var/www/app/documents/」;
const ファイル名=「../../../../../../etc/passwd「;
const normalizedPath = path.normalize (path.join (ベースフォルダ、ファイル名));
//セキュア:/var/www/app/documents/passwd を読み取ります
const data = fs.readFileSync (正規化パス、'utf8');
ジャバスクリプト-セキュア-ファイル名
この例では、パスのファイル名部分のみを取得して、指定されたフォルダーからトラバースできないようにすることで、パストラバーサルを防止しています。
定数 fs = 必須 (「fs」);
定数パス = 必須 (「パス」);
const BaseFolder =「/var/www/app/documents/」;
const ファイル名 = パス.ベース名 (「../../../../../../etc/passwd「);
//セキュア:/var/www/app/documents/passwd を読み取ります
const data = fs.readFileSync (path.join (BaseFolder, FileName), 'utf8');
パイソン-安全でない
フルパスを解決しないか、パスのファイル名部分のみを使用するようにすると、コードがパストラバーサルの影響を受けやすくなります。
BaseFolder =「/var/www/app/documents/」
ファイル名 =「../../../../../etc/passwd」
# 安全ではない:/etc/passwd を読み込む
ファイルコンテンツ = 開く (ベースフォルダ+ファイル名) .read ()
Python-セキュア-カノニカル
この例では、フル (絶対) パスを解決し、ファイル解決パスがベースフォルダ内にあることを確認することで、パストラバーサルを防止しています。
os.path をインポート
BaseFolder =「/var/www/app/documents/」
ファイル名 =「../../../../../etc/passwd」
正規化パス = os.path.normpath (ベースフォルダ+ファイル名)
# SECURE: 指定されたベースフォルダ外のファイルを読み込もうとすることを拒否します
正規化されていない場合はパス。(ベースフォルダ) で始まる:
「ベースフォルダを読み出そうとしています」を返す
# セキュア:/var/www/app/documents/passwd を読み込む
ファイルコンテンツ = オープン (正規化されたパス) .read ()
Python-セキュア-ファイル名
この例では、パスのファイル名部分のみを取得して、指定されたフォルダーからトラバースできないようにすることで、パストラバーサルを防止しています。