ディレクトリトラバーサル攻撃への対処

ディレクトリトラバーサル攻撃を検知する場合「../」を検知するとかではダメで以下のIPAのサイトにあるようにちゃんとしたチェックが必要。
http://www.ipa.go.jp/security/awareness/vendor/programming/b07_07.html

必要なのは以下の精査。

  • パス名を正規化し、正規化する前と比較し一致すること
  • パス名の各階層のディレクトリが実際に存在すること
  • パス名の各階層のディレクトリのオーナやグループ,ファイルモードが適切であること
  • パス名中にシンボリックリンクが含まれていないこと

このうち

  • パス名の各階層のディレクトリが実際に存在すること
  • パス名の各階層のディレクトリのオーナやグループ,ファイルモードが適切であること

の2件は普通にチェックするだけなのだが、シンボリックリンクかどうかを検証するのと、
正規化したものが一致するかどうかを検証するのが意外に難しい。
そしてぐぐってもなかなかサンプルが見つからない。
みんなどうやって実装しているのだろうか。コモンズとかに便利なのあったりすんの??

パス名を正規化し、正規化する前と比較し一致することのチェック

まず正規化のおさらい

  • 絶対パス名である(/ から始まる)
  • パス名中に// を含まない(/home//foo/data は/home/foo/data と等価)
  • パス名中に/./ を含まない(/home/./foo は/home/foo と等価)
  • パス名中に/../ を含まない(/home/foo/../bar/ は/home/bar/ と等価)
http://www.ipa.go.jp/security/awareness/vendor/programming/b07_07.html

さらにWindowsも考えると

  • 絶対パス名である ([a-zA-Z]:\ から始まる)
  • パスの区切りは「/」もしくは「\」である

の2点の考慮も加えなければなりません。
これがやっかいでドライブ名は大文字でも小文字でも良いし区切り文字も変わるのでここを置き換えてから比較してあげる必要がある。
※しかしパス全体を大文字とかにしてはダメ。Unixは大文字小文字を区別する

なのでこんな感じ?

    private boolean isValidPath(String path) throws IOException {
        String retPath = path.substring(0, 1).toUpperCase() + path.substring(1);
        String canPath = new File(path).getCanonicalPath().replace('\\', '/');
        return canPath.equals(retPath.replace('\\', '/'));
    }

パス名中にシンボリックリンクが含まれていないことのチェック

ふつーに考えると上のCanonicalPathとの比較でシンボリックリンクもチェックできてるんじゃないかと思ってしまうのですが
Unixの場合親ディレクトリにシンボリックリンクが含まれる場合も考えられるのでそれだけでは足りない。
なので親ディレクトリもチェックしてあげる必要あり。

なのでこんな感じ?

    private boolean isSymLink(String path) throws IOException {
        File baseFile = new File(path);
        File file = new File(baseFile.getAbsoluteFile().getParentFile().getCanonicalFile(),
                baseFile.getName());
        return !file.equals(file.getCanonicalFile());
    }

これでWindowsでもUnixでも判定できそうだ。


注、使用する場合などは各位ちゃんと検証をしてくださいね!これによって生じた損害に対して私は一切の責任を負いませんよ!