第7章 データ漏えい対策
テンポラリファイル(Unix の一時ファイル)
テンポラリファイルにデータを一時的に保持する際、重要な情報が含まれる場合は /tmp 等共通のディレクトリの使用を避けた方がよい。個人情報、暗号鍵、パスワード等は特にそうである。
/tmp 等のシステム共通のディレクトリはそのコンピュータのほとんどすべてのアカウントから読み書きできるため、情報が漏えいしやすい。また、シンボリックリンク攻撃を受けるおそれがある。
重要な情報を一時ファイルに書き込む処理のあるプログラムが対象となる。

テンポラリファイル対策
テンポラリファイルとして格納したデータが漏えいしてしまう問題は、長期的に保管するデータでは無く、一時的に利用するだけという扱いにより、十分な注意が向けられないことから発生している。この対策としては、まず、テンポラリファイルに格納するデータに重要な情報(個人情報や暗号鍵、パスワード等)が含まれないようにすることであるが、重要な情報の格納が必要な場合には、次のような項目に注意する必要がある。
- プログラム専用のテンポラリディレクトリを使う
- tmpnam() 等の脆弱性のある関数は使用しない
- 予測困難なファイル名にする
- 厳重なアクセス権を選定する
- シンボリックリンクをチェックする
- ファイルを作成したら unlink する
プログラム専用のテンポラリディレクトリを使う
サービスを提供するデーモンプログラム等は、/tmp 等の共通のディレクトリを使用せずに、次のようにプログラム専用のテンポラリディレクトリを作成し、そこにテンポラリファイルを作成するようにする。
- プログラム専用のユーザを作成する
- プログラム専用のテンポラリディレクトリを作成する
- テンポラリのディレクトリは、プログラム専用のユーザのみがアクセス可能にする
このようにすると、他のユーザからの干渉が避けられ、情報は漏洩しにくくなる。
使用を避けた方がよい関数
以下の3つの関数は、現在でも使用できるが、脆弱性があるため使用を避ける。
- tmpnam(3), tempnam(3)
既に存在するファイル、シンボリックリンクとの排他が取られない。 - mktemp(3)
生成するファイル名が推測可能である。
その代わりに、以下に述べらている脆弱性が解消された mkstemp(3) 又は tmpfile(3) を使用するようにする。
予測困難なファイル名にする
mkstemp() 関数や tmpfile() 関数を使うことで,安全で適切なテンポラリファイルの作成が可能である。これらの関数は以下の3つの注意点をすべて網羅している。
- アクセス権限は自分のみに与える
- 同名のファイル(やシンボリックリンク)が存在しないことを確認する
- ランダムで予測困難なファイル名を生成する
1) int mkstemp(char *template);
mkstemp()関数はテンポラリファイルを作成,オープンし,ファイル記述子を返す。mkstemp()関数の引数には作成するテンポラリファイルのファイル名のテンプレートを渡す。テンプレートは
/tmp/my_tmp_file_XXXXXX
といった文字列で,文字列の最後は6つの「X」で終わる必要がある。この「XXXXXX」部分が自動的にランダムな文字列に置き換えられ,ランダムな名前のテンポラリファイルが作成される。テンプレートに
/mydaemon_dir/tmp/prefix_XXXXXX
といった文字列を与えると,デーモン専用のテンポラリファイルディレクトリ「/mydaemon_dir/tmp/」にテンポラリファイルを作成することもできる。
char filepath[] = "/tmp/my_file_XXXXXX"; ↑6 文字の'X'が必要 fd = mkstemp(filepath);
filepath には、 'XXXXXX'が変換されて "/tmp/my_file_PvQLMk" のような文字列が返される。
2) FILE *tmpfile(void);
tmpfile()関数はmkstemp()関数を使いやすくした位置付けのものである。具体的には次のような動作をする。
- mkstemp()関数により/tmpディレクトリ下にテンポラリファイルを作成する
- unlink()システムコールによりファイル削除する
- fdopen()関数によりFILE*を取得する
厳重なアクセス権を選定する
テンポラリファイルのオープンは、本人のみが読み書きできるようにし、排他モードでオープンする。
テンポラリファイルを open() により作成するのであれば、次の例のようにパラメータを指定する。
fd = open(path, S_IRUSR | S_IWUSR, O_CREAT | O_RDWR | O_EXCL); ・S_IRUSR|S_IWUSR (=0600) S_IRUSR (=0400) ユーザーに読み込みを許可する S_IWUSR (=0200) ユーザーに書き込みを許可する ・mode = O_CREAT|O_RDWR|O_EXCL O_CREAT ファイルが無ければ作成する O_RDWR 読み書き用にオープンする O_EXCL ファイルが既に存在したらエラーとする (O_CREATと合わせて使用する) O_CREAT|O_RDWR|O_EXCL ファイルを作成オープンし, すでに存在したらエラーとする
シンボリックリンクをチェックする
シンボリックリンク攻撃は、攻撃者が /tmp 等に読み書きするファイルを予測し、そのファイルが別なファイルを指すようにシンボリックリンクを張り、別なファイルにアクセスするように仕向けるものである。
シンボリックリンク攻撃の対策については、本コンテンツの別記事「シンボリックリンク攻撃対策」を参照されたい。
ファイルを作成したら unlink する
テンポラリファイルを作成し、使用し終わったらクローズ close() し、削除 unlink()するのが一般的だが、テンポラリファイルを別なプログラムで使用しないのであれば、次のようにするとより安全になる。
ファイルを作成 open() 直後に unlink() してしまう。unlink() してしもディレクトリから消えるのみで、ファイル自体にはアクセス可能であり、ファイル自体は存在し続けるが,どのディレクトリにも属さない状態となる。つまりファイル名が存在しなくなり、このファイルは二度と open() できなくなる。unlink() 以降はそのファイルへアクセスできるのは,unlink() 以前に open() したプロセスのみとなる。
このように open() の直後に unlink() されたファイルは、プロセスが異常終了した時には、既に unlink(2) されているので残骸が残ることもなくなる。具体的には下記の例のようにする。
#define TMPFILE "/tmp/my_tmp_file" fd = open(TMPFILE, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); if(fd==-1) exit(1); unlink(TMPFILE); ..... fd を使用して write() 等を行う。
スティッキービット
過去のUnixシステムでは、所有者が root の /tmp のファイルを一般ユーザが削除できてしまっていた。現在のUnixシステムでは、ディレクトリの属性にスティッキービットを on にすることで、この問題を解決している。
drwxrwxrwt 5 root root 4096 Dec 5 04:24 /tmp ↑ t:スティッキービット
このビットが立っていると、以下のいずれかの条件に合致しないと、そのディレクトリ以下にあるファイルの削除権限が与えられない。
- ファイルの所有者であること
- ディレクトリの所有者であること
- rootであること
Windowsのテンポラリファイル
Windowsの場合、mkstemp() のような関数は Win32 APIには存在しない。Win32 API の GetTempPath() 関数により、テンポラリファイルのディレクトリを取得できるが、ランダムなファイル名を作成する APIは用意されていない。このため信頼性のある乱数等を使用して、ファイル名を作成する必要がある。
※GetTempFileName() という関数があるが、これはファイル名の一意性を保障するのみで、推測困難なランダムなファイル名を作成するわけではない。