公開日:2007年9月26日
独立行政法人情報処理推進機構
セキュリティセンター
本ページの情報は2007年9月時点のものです。
記載の資料は資料公開当時のもので、現在は公開されていないものも含みます。
入力パラメータとしてファイル名またはパス名を扱う場合、細工されたファイル名が与えられて、所定のディレクトリの範囲外のファイルへユーザが不正にアクセスするおそれがある。
ひとつのファイルを識別するための名前──ファイル名、あるいはファイル名がディレクトリ修飾されたパス名──には異なる表現が複数通りあり得、攻撃者はそうした「別名」を使って予定外のファイルにアクセスしてくる。ファイル名の入力検査においては受け入れるべきでないパス名のパターンをすべて排除する必要がある。
ファイルを特定する方法には、ディレクトリ修飾をする方法とファイル名のみでの方法の2種類がある。
例
"/etc/passwd"
"/bin/ls"
例
"passwd"
"ls"
ファイルを特定するパス名のディレクトリ修飾には、上記(1)の通常の指定方法のほかにカレントディレクトリからの相対パスによって修飾する方法がある。
(カレントディレクトリを示す「./」を含む)
例
"./passwd"
"./etc/passwd"
(ひとつ上のディレクトリを示す 「../」 を含む)
例
"../../etc/passwd"
"foo/../../../etc/passwd"
パス名にディレクトリ修飾を許す場合、上記(2)のような相対パスが混入されて引きおこされるディレクトリトラバーサル攻撃(注釈)を防ぐためにも、パス名の正規化チェックが必要となる。
Windowsにおけるファイル名とパス名には、上記ファイル名・パス名の一般的特性の(1)および(2)にあるようなディレクトリ修飾に加えて、次のような特性がある。
8.3形式のショートネームは、8byteの名前と3byteの拡張子からなるファイル名である。
Windows95以降では、255byte(UNIXの場合は256byte)までのファイル名が使用可能になったが、以前の古いソフトウェアとの互換性を考慮して、OS内部では自動的に生成した8文字の名前を保持している。
[例 「dir /x」の出力]
2006/11/10 11:56 370 foo1.txt
2006/11/10 11:57 426 LONG_F ̄1.TXT LONG_FILE_NAME.TXT
↑8.3形式 ↑ロングネーム
8.3形式のショートネームのパス名からロングネームのパス名を取得する Win32 API には、GetLongPathName が用意されている。
ファイル名の末尾の「.」や半角スペースは、省かれるため、下記の表記はいずれも同じ実体のファイル 「foo2.txt」として扱われる。
1. "foo2.txt."
2. "foo2.txt "
3. "foo2.txt. . ."
Windows のNTFSでは、ファイル名中の「:」はNTFSファイルの「ストリーム」を表す区切り記号として意味をもつ。
ストリームを使うと、あるファイルの付随するデータをもたせることができる。
次の cygwin 環境の例では、最初に foo.txt を作成し付随するデータをストリーム stm1, stm2にもたせている。
$ echo hello > foo.txt ← foo.txt を作成
$ ls -l foo.txt
-rw-r--r-- 7 Nov 10 13:48 foo.txt ← ファイルサイズは 7 byte
$
$ echo "Streem TEST." > foo.txt:stm1 ← foo.txtにストリームstm1を追加
$ echo "Streem TEST2." > foo.txt:stm2 ← foo.txtにストリームstm2を追加
$
$ cat foo.txt
hello
$ cat foo.txt:stm1
Streem TEST.
$ cat foo.txt:stm2
Streem TEST2.
$
$ ls -l foo.txt
-rw-r--r-- 7 Nov 10 13:50 foo.txt ← ファイルサイズは 7 byte のまま日付のみ更新されたように見える
プログラムのアクセスするディレクトリを制限させるには、下記のようにパス名をチェックする対策を行う必要がある。
扱うファイル名にシンボリックリンクを許す場合とそうでない場合がある。いずれの場合も、ファイル名が指し示すファイルの実態が、所定のものであることを確認するロジックが必要である。
シンボリックリンクに関する話題は『シンボリックリンク攻撃対策』を見られたい。
Windows の場合も上記Unix, GNU/Linux におけるパス名検査の(1) 1 ~ 4の方法が有効である。ただし、次の点を考慮に入れる。
多くの自由度をもつWin32のパス名を相手にするには複雑なロジックのプログラムが必要になるが、パス名の解釈に伴う「拡張機能」の多くを不活性化する特殊な指定方法がある。それはパス名の先頭に\\?\という4文字を付け加えることである。例えば、
pfile = fopen ("d:\\dir\\data.txt", "r");
の代わりに
pfile = fopen ("\\\\?\\d:\\dir\\data.txt", "r");
のようにするのである。
\\?\から始まるパス名については拡張機能が次のようにはたらかなくなる。
Windowsのファイル名には、下記のような予約されたデバイス名がある。パス名の構成要素──ファイル名あるいはディレクトリ名──のひとつでもこれらの綴りと一致していると、そのパス名はデバイスを表すものと解釈され、ファイルではなく、デバイスがオープンされ、プログラムは限りなく入力待ちになる。したがって、これらの綴りがパス名の構成要素に現れていないことを検査する必要がある。
AUX, CON, NUL, PRN, COM1~COM9, LPT1~LPT9
なお、AUX.txt のように拡張子がついた綴りもデバイス名と解釈されるので注意が必要である。
通常のディレクトリ修飾やファイル名のみでの指定を期待していると、相対パス修飾による想定していないファイルへの不正アクセスを許してしまうおそれがある。これは、ファイル名の表現が複数通りあり得ることが原因である。
対策としては、このようなファイル名の「別名」を指定できないように排除する必要がある。また、Windowsでは、Windowsならではの特性もあるので、この特性にも注意を向ける必要がある。