web-dev-qa-db-ja.com

node.jsのパスを結合するときにディレクトリトラバーサルを防ぐ方法は?

安全な方法で2つのパスを連結する必要があるnode.js webappがあります。最初の1つ(左端)は定数で、2番目の1つ(右端)は最初の1つを基準にしており、信頼できないユーザー入力からのものです。結果のパスは、最初のパスの下にあるものでなければなりません。だから状況はこれです:

_path1 = "public/html";                // Hardcoded path.
path2 = req.query.path;               // Untrusted user input.
result = safePathJoin(path1, path2);  // Result can be e.g. public/html/index.htm,
                                      // but never private/config.xml
_

私が必要なのは、ディレクトリトラバーサル攻撃に対して安全な関数safePathJoin()です。私の最初の素朴なアプローチはこれです:

_safePathJoin = function(path1, path2) {
    path1 = path.normalize(path1);
    var result = path.join(path1, path2);
    return result.startsWith(path1) ? result : undefined;
}
_

これで十分ですか?これを行う標準的な方法はありますか?助言がありますか?

13
Anders

この状況で私が使用したアプローチの1つを次に示します。

  1. path.normalize() はすべての_._と_.._を処理するので、どちらかが存在する場合、それがパスの先頭にあることを確認できます。
  2. パスの先頭から_../../_を削除します。

そう:

_var safeSuffix = path.normalize(unsafeSuffix).replace(/^(\.\.(\/|\\|$))+/, '');
var safeJoin = path.join(basePath, safeSuffix);
_

あなたのアプローチについて:プレフィックスをチェックすることは私にはかなり良い考えのようです。私はあなたの実装で見られるいくつかの問題があります:

  • 末尾にスラッシュが付いていない接頭辞を確認しました:_../html-other_は_public/html-other_に解決されますが、これはあなたが望むものではないと思います。
  • Windowsシステムで問題が発生すると、.normalize()が_/_を_\_に変換します。つまり、noパスが機能します。

(わずかに異なる状況で)接頭辞チェックを実行すると、次のようになります。

_function checkPrefix(prefix, candidate) {
    // .resolve() removes trailing slashes
    var absPrefix = path.resolve(prefix) + path.sep;
    var absCandidate = path.resolve(candidate) + path.sep;
    return absCandidate.substring(0, absPrefix.length) === absPrefix;
}
_

(はい、両方に_path.sep_を追加して、プレフィックスdir自体がテストに合格するようにしました。)

14
cloudfeet

定義されたpathディレクトリのサブディレクトリであるユーザー入力rootを受け取り、ユーザーがroot + pathディレクトリ、私にとって最も安全なオプションは、絶対パスのみを入力として許可し、特別なディレクトリ名...)入力パスは元の入力と同じです:

// Input

const path1 = "public/html";
const path2 = req.query.path;

// Checkout

const isNotSpecialDirName = part => !(['', '.', '..'].includes(part));

const path2Clean = path2.split(path.sep).filter(isNotSpecialDirName).join(path.sep);

// Output

if (path2Clean !== path2) {
  // Obfuscated with Not Found
  throw new Error('Not Found');
}

const result = path.join(path1, path2Clean);
0
Damaged Organic