../ の除去処理の回避
Linux 上で動作する Web アプリケーションに,パラメタ file で指定されたファイルの内容を返す次の処理がある。replace は,文字列を先頭から 1 回だけ走査し,一致した "../" をそれぞれ空文字列に置き換える。file には URL デコード後の値が渡され,/etc/passwd はアプリケーションの実行ユーザが読み出せるものとする。
String name = request.getParameter("file");
name = name.replace("../", "");
File f = new File("/var/app/docs/" + name);
// f の内容を応答として返す攻撃者が /etc/passwd の内容を読み出せる file の値はどれか。
- ア../../../etc/passwd
- イ/etc/passwd
- ウ....//....//....//etc/passwd
- エ..\..\..\etc\passwd
正解と解説を見る
正解 ウ
この問題の基礎知識
- ディレクトリトラバーサルは,ファイル名に「../」などを含めて,意図したディレクトリの外にあるファイルを読み書きさせる攻撃である。
- 特定の文字列を 1 回だけ走査して除去する処理では,除去した結果として前後の文字がつながり,除去したはずの文字列が新たにできることがある。
- Linux のパスの区切り文字は「/」であり,「\」は通常の文字として扱われる。また,連続した「/」は一つの「/」と同じに扱われる。
- 根本的な対策は,basename などでディレクトリ名を除いたファイル名だけを用いることや,正規化した後のパスが基準のディレクトリの中にあることを確認することである。
三つの "../" はいずれも replace で除去され,name は etc/passwd になる。開かれるのは /var/app/docs/etc/passwd であり,基準のディレクトリの外には出られない。
この処理は固定のディレクトリ名の後ろに文字列を連結しているので,絶対パス名を与えても /var/app/docs//etc/passwd となり,/var/app/docs/etc/passwd を指す。open(filename) のように値をそのまま使う実装とは異なる。
"....//" の中央にある "../" が除去されると,前の ".." と後ろの "/" がつながり "../" が新たにできる。name は ../../../etc/passwd となり,/etc/passwd を指す。除去による無害化に頼らず,basename でファイル名だけを取り出すなどの対策が必要である。
Linux ではバックスラッシュはディレクトリの区切り文字ではないので,この値は全体が一つのファイル名として扱われ,/var/app/docs の中のファイルを指す。Windows 上で動作する場合には区切り文字として解釈され得る。
照合の根拠(5 件)
- イ(誤り)・ウ(正解): 固定ディレクトリを前置すれば絶対パス名の指定は防げるが,「../」を使った攻撃は防げない
あらかじめ固定のディレクトリ「dirname」を指定し、open(dirname+filename) のような形でコーディン グする方法があります。しかし、これだけでは、「../」等を使用したディレクトリ・トラバーサル攻撃を回 避できません。
IPA 安全なウェブサイトの作り方 改訂第 7 版 · 1.3 根本的解決 3-(i)-b(新しいタブで開きます) - ウ(正解): 対策として basename でファイル名だけを取り出す
basename() 等の、パス名からファイル名のみを取り出す API を利用して、open(dirname+basename(filename)) のような形でコーディングして、filename に与え られたパス名からディレクトリ名を取り除くようにします
IPA 安全なウェブサイトの作り方 改訂第 7 版 · 1.3 根本的解決 3-(i)-b(新しいタブで開きます) - ウ(正解)・ア(誤り): replace は先頭から末尾へ 1 回走査して置換するので,置換後に新たにできた "../" は除去されない
replacement proceeds from the beginning of the string to the end, for
OpenJDK java.lang.String · replace(CharSequence, CharSequence) の Javadoc(新しいタブで開きます) - ウ(正解): 疑わしい文字列を除去する「クリーニング」は意味を変えてしまい得る
Denylisting or “cleaning” input: Apply field-specific acceptance rules. Removing suspicious characters can change meaning
OWASP Input Validation Cheat Sheet · Common Pitfalls(新しいタブで開きます) - エ(誤り): Linux のパス名の要素は「/」で区切られるので,バックスラッシュは区切り文字にならない
where a component is a substring delimited by \(aq/\(aq characters, this component is looked up in the current lookup directory.
Linux man-pages path_resolution(7) · Step 2: walk along the path(新しいタブで開きます)
Java 21 で検算: s.replace("../","") の結果と new File("/var/app/docs/"+n).getCanonicalPath() は,ア→etc/passwd→/var/app/docs/etc/passwd,イ→/etc/passwd→/var/app/docs/etc/passwd,ウ→../../../etc/passwd→/etc/passwd,エ→変化なし→/var/app/docs/..\..\..\etc\passwd。