第9章 ファイル対策
ファイル拡張子による起動

ファイルの拡張子とプログラム

ファイルの拡張子は、そのファイルが記録しているデータの種類を示すと同時に、どのアプリケーションプログラムによってそのファイルが処理されるべきかということと深い関係がある。

特にWindowsでは、ファイル名の拡張子とその拡張子をもつファイルを処理する際起動すべきプログラムの対応を保持している。APIを通じて、プログラムを直接起動するのではなく、ファイルの拡張子にもとづいてプログラムを起動するようオペレーティングシステムに依頼することができる。

異なる複数種類のファイルを取り扱うアプリケーション、例えば、ブラウザ、メーラ、ファイルビューア、マルチメディア再生ソフト等においては、このようなAPIの利用は便利である。

しかし、拡張子とプログラムの対応付けが何者かによって不正に変更されると、想定外のプログラムが起動されてセキュリティ侵害が生じるよう仕向けられるおそれがある。

例えば、拡張子が .jpg であり、これによって見かけ上は JPEG 画像に見えるファイルが、実は VBScript のスクリプトを含んでおり、VBScript エンジンによって悪意のスクリプトが実行されてしまう、といった問題である。

図9-4: ファイル拡張子による起動

拡張子にもとづくプログラム起動API: ShellExecute()

Windowsプラットフォームには、Windowsエクスプローラでユーザがファイルアイコンをダブルクリックして開くことに相当する処理をアプリケーションプログラムから行うことのできる、ShellExecute() というAPI関数がある。

ファイルの拡張子に関連づけられたアプリケーションを起動するAPI関数の ShellExecute() は、次のような呼び出しインタフェースをもつ。

HINSTANCE ShellExecute(
   HWND hwnd,              // 親ウィンドウのハンドル
   LPCTSTR lpVerb,         // 操作("edit", "explorer", "open" 等)
   LPCTSTR lpFile,         // 操作対象ファイル(開く時のファイル名、"explorer" 等)
   LPCTSTR lpParameters,   // 操作対象ファイルのパラメータ
   LPCTSTR lpDirectory,    // 既定のディレクトリ
   INT nShowCmd            // 表示方法(非表示、最小化、最大化等を指定できる)
);

拡張子にもとづくプログラム起動における対策

拡張子にもとづいてプログラムを自動で選定し実行する際、想定外のプログラムが起動されることを避けるためには次の対策が考えられる。

(1) ShellExecute() を使用しない選択

ひとつの選択肢として、拡張子にもとづいて起動するプログラムを特定するロジックを自ら記述し、ShellExecute()を使用しないという対策が考えられる。

この方式では、このAPIの利便性を享受できなくなるが、オペレーティングシステムレベルで拡張子とプログラムの対応付けを変更されても影響を受けずに済む。

次節以降には、ShellExecute() を使用する場合の対策を述べる。

(2) 利用できる拡張子を限定する

あらゆる拡張子のファイルの取り扱いを許すのではなく、一定の拡張子をもつもののみを処理するよう、ShellExecute() 呼び出しの前に制限ロジックを入れる。

受け入れる拡張子のリストをもつ必要があるが、それには次の方法が考えられる。

前者は、柔軟性に欠けるが外部からの干渉に強い。後者の場合は、定義ファイルを攻撃者の改ざんの手から保護する工夫が必要である。

(3) ファイル内容の特徴を確認する

偽装されて、ファイルの内容と拡張子が表すデータタイプが食い違っていることを、ファイルの内容(主に先頭部分)の検査によって検出を試みる。

例えば、拡張子 .bmp のWindowsビットマップファイルの先頭部分は次のようなフォーマットになっている。拡張子 .bmp をもつファイルが、これと合致しない内容をもっていれば、拡張子が偽装された悪意のファイルであると見なすのである。

ビットマップファイル(.bmp)の先頭部分のフォーマット
バイト数内容
2識別子("BM")
4ファイルサイズ、バイト数
2予約領域1 (値 0)
2予約領域2 (値 0)
4ビットマップデータの開始位置、バイトオフセット

(4) ユーザに警告する

ファイルを開く直前に、プログラムを起動してファイルを処理させてよいか確認するようにする。例えば、次のようなダイアログボックスを表示し、ユーザの判断を仰ぐ。

セキュリティ警告
ファイル xxxxxxxx をプログラム yyyyyyyy を使って開こうとしています。このファイルが信頼できる場合のみ実行して下さい。
[実行] [中止]

ShellExecute() 使用上の注意

ShellExecute() を使用する際にはパス名の末尾に関する次のような注意も必要である。

(1) 開くファイルの拡張子を省略しない

このAPIでは、拡張子を省略することができるが、その省略は行わないようにする。理由は、拡張子が異なる同名のファイルが存在するとき、意図しないほうのファイルが開かれるおそれがあるからである。

例 開こうとするフォルダに、foo.bat foo.txt がある場合に、foo.txt を開くのであれば、次のようにする
ShellExecute (NULL, NULL, "foo.txt", NULL, NULL, SW_SHOWDEFAULT);

(2) フォルダを開くときの末尾の「\」を省略しない

ShellExecute() でフォルダ(ディレクトリ)のウィンドウを開くことができるが、それを行う際、パス名末尾に必ず「\」を付けるようにする。理由は、フォルダ名と同名のファイル(拡張子は異なる)があると、ファイルの方が開かれるおそれがあるからである。

例 フォルダ「c:\tmp」とファイル「c:\tmp.exe」がある場合、次のようにすると、「c:\tmp.exe」が実行されるおそれがある
ShellExecute (NULL, NULL, "c:\\tmp", NULL, NULL, SW_SHOWDEFAULT);

フォルダ c:\tmp を開くのであれば、次のようにする

例 フォルダ「c:\tmp」とファイル「c:\tmp.exe」がある場合、次のようにすると、「c:\tmp.exe」が実行されるおそれがある
ShellExecute (NULL, NULL, "c:\\tmp\\", NULL, NULL, SW_SHOWDEFAULT);

まとめ

ファイルの拡張子にもとづき、プログラムが自動で選ばれて実行される仕組みは便利である。しかしそこには、設定が変えられて不正なスクリプトが実行される等の攻撃が入り込む余地がある。ファイルの種類に対応したプログラムを起動させる場面では、あらかじめファイルの妥当性を検査する等の対策を施す。