• 検索結果がありません。

1 $ cat aboutipa 2 IPA is a Japanese quasi-government 3 organization established in accor- 4 dance with The Law for Information 5 Processing Technolog

N/A
N/A
Protected

Academic year: 2021

シェア "1 $ cat aboutipa 2 IPA is a Japanese quasi-government 3 organization established in accor- 4 dance with The Law for Information 5 Processing Technolog"

Copied!
12
0
0

読み込み中.... (全文を見る)

全文

(1)

● ● ●

[7-8.]

テンポラリファイルから情報が漏れる

無権限者の干渉を受けやすい/tmp上へのファイルの作成を避ける,シンボリッ クリンクによるすり替えを警戒するなど,Unix上のアプリケーションでテンポ ラリファイルを取り扱うときの問題と安全な手法について説明する。 第 7 章 セキュア Unix/Linux プログラミング

テンポラリファイル

テンポラリファイルとはプログラムが一時的にデータを保存するファイルである。テンポラリファイルを使 用するサンプルプログラム upper の実行例を実行例1に示す。upper はファイルの中身の英小文字をすべて英 大文字に変換するプログラムである。まず 1 行目で「aboutipa」ファイルの内容を cat コマンドで表示してい る。ファイルの内容は英大文字と英小文字が混在する英文である。7 行目でこのファイルを引数として upper コマンドを実行している。8 行目の cat コマンドの結果が示すとおり,「aboutipa」ファイルの内容は英小文字 がすべて英大文字に変換されている。 注意すべきこのプログラムの特徴は,与えられたファイルそのものを書き換えているということである。こ のサンプルに限って言えば,ごく単純な題材を選んでいるためプログラミングは難しくないが,一つのファ イルから読み込みつつその同じファイルに直接書き込むという処理は一般にはとても厄介なものである。テ ンポラリファイルを使用すれば,こうした面倒な問題を考慮せずに済む。実行例1のサンプルプログラム upper のソースリストをリスト1に示す。リスト1は,まず与えられたファイルから 1 文字ずつ読み込みな がら大文字に変換し,テンポラリファイルに結果を書き出す。最後に,与えられたファイルへテンポラリファ イルをリネームすることで,与えられたファイルの内容がそのまま大文字に変換されたように見せかける。 リスト1について解説する。4 行目で作成するテンポラリファイルのファイル名を「/tmp/my_tmp_file」と指

1 $ cat aboutipa  ←aboutipa ファイルの内容を表示

2 IPA is a Japanese quasi-government 3 organization established in

4 dance with The Law for Information ←大文字と小文字が混在している

5 Processing Technology Promotion, 6 (Law No.90, May 22, 1979).

7 $ ./upper aboutipa  ←プログラム upper を実行,aboutipa 中の小文字を大文字に変換

8 $ cat aboutipa  ←aboutipa ファイルの内容を表示

9 IPA IS A JAPANESE QUASI-GOVERNMENT 10 ORGANIZATION ESTABLISHED IN

11 DANCE WITH THE LAW FOR INFORMATION ←すべて大文字に変換されている

12 PROCESSING TECHNOLOGY PROMOTION, 13 (LAW NO.90, MAY 22, 1979).

(2)

定している。/tmp ディレクトリは一般的にテンポラリファイルを置く場所としてよく知られている。また 「my_tmp_file」というファイル名は私以外には使わないだろう・・・という勝手な期待により決めている。14 行目で与えられたファイルをオープンする。18 行目でテンポラリファイルを作成する。fopen( )関数はファイ ルが存在しないとき,自動的にファイルを作成してくれる。22 ∼25 行目で,与えられたファイルから 1 バイ トずつ読み込み,英小文字を英大文字に変換し,テンポラリファイルへ書き出す。すべての内容を変換し書 き出すまで while ループが回りつづける。28 ∼ 29 行目で両ファイルをクローズする。最後に 32 行目でテン ポラリファイルを与えられたファイルへリネームする。テンポラリファイルをリネームするので,テンポラ リファイルを削除する手間も省ける。このようにテンポラリファイルを使用することで,すっきりとした実 装が可能となる。

テンポラリファイルから情報が漏れる

実はリスト1にはセキュリティホールが存在する。実行例2は悪意あるユーザが /tmp/my_tmp_file を監視す るときの,簡単な perl プログラムの実行例である。プログラム自体は 1 行目の perl コマンド 1 行のみである。 for ループで「cat /tmp/my_tmp_file 2>/dev/null」を繰り返しているだけである。高速に「cat /tmp/my_tmp_file」 を繰り返すことにより,/tmp/my_tmp_fileが作成された瞬間にその内容を表示するという仕掛けである。もち ろん /tmp/my_tmp_file が作成され削除されるまでの瞬間に,タイミングよく「cat /tmp/my_tmp_file」が実行さ

れなければ表示されない。「2>/dev/null」は /tmp/my_tmp_file が存在しないときのエラーメッセージを表示し

ないための工夫である。

悪意あるユーザは実行例2の 1 行目の perl プログラムを実行しながらじっと誰かがリスト1のプログラムを

実行するのを待つ。他のユーザがリスト1のプログラムを実行すると,(タイミングが合えば)テンポラリファ

イルの内容が悪意あるユーザの端末で 2 行目∼ 6 行目のように表示される。このようにテンポラリファイル 1 /* Usage: upper file */

2

3 #include <stdio.h>

4 #define TMPFILE "/tmp/my_tmp_file" 5

6 int main(int argc, char* argv[]) 7 { 8 FILE *fp; 9 FILE *tmpfp; 10 int c; 11 12 /* 元ファイルオープン */ 13 if(argc!=2) exit(1); /* コマンド引数チェック */ 14 fp = fopen(argv[1], "r"); 15 if(fp==NULL) exit(1); /* オープン失敗 */ 16 17 /* テンポラリファイル作成 */ 18 tmpfp = fopen(TMPFILE, "w"); 19 if(tmpfp==NULL) exit(1); /* オープン失敗 */ 20 21 /* 元ファイル→大文字変換→テンポラリファイル */

22 while( (c=fgetc(fp)) != EOF ) {

23 if(c>='a' && c<='z') c-=0x20; /* 英小文字→英大文字変換 */

24 if(fputc(c, tmpfp)==EOF) break; 25 } 26 27 /* ファイルクローズ */ 28 fclose(tmpfp); 29 fclose(fp); 30 31 /* テンポラリファイル→元ファイルへリネーム */

32 if(rename(TMPFILE, argv[1])==-1) perror("rename"); 33

34 return 0; 35 }

(3)

からユーザが扱うファイルの内容が漏洩する。 リスト1をもう一度見てみよう。22∼29行目の処理の間,テンポラリファイルが存在する。このテンポラリ ファイルは 32 行目が実行されることで消滅する。よって 22 ∼ 29 行目の処理の間にテンポラリファイルの中 身が読み取られることで,情報漏洩が発生する。このセキュリティホールの要因は次の 2 点である。 ・他のユーザからもテンポラリファイルの内容を読み取ることができた ・テンポラリファイルのファイル名が既知であった 作成するテンポラリファイルのファイルモード(注1)は 600 で十分であるはずだが,18 行目の fopen( )関数を 使用すると最悪の場合,ファイルモードは 666 でファイルが作成されてしまう。これが他のユーザへも読み 取り権限を与えてしまった要因である。またファイル名が「/tmp/my_tmp_file」で固定であるため,悪意ある ユーザは監視すべきテンポラリファイルを容易に特定できてしまった。これが実行例2のような簡単なプロ グラムで待ち伏せできた要因である。テンポラリファイルのファイル名が毎回ランダムに変化し十分予測困 難であれば,実行例2のような簡単なプログラムでは干渉できない。 (注1・ファイルモードとはファイルへのアクセス権設定のことである。ご存知の方も多いと思うが,Unix/Linux ではファイルアクセス権設定のビットパターンを 600 とか 755 といった 8 進数で表す。例えば 600 は,そのファ イルの所有者のみに読み出しと書き込みを許可するという意味である。)

テンポラリファイルを介したシンボリックリンクアタック

実はリスト1にはもう一つ深刻なセキュリティホールがある。悪意あるユーザが実行例3のコマンドを実行 して,/etc/passwdファイルへのシンボリックリンクを /tmp/my_tmp_fileとして作った場合に問題が発生する。 もしこの状態で,root ユーザがリスト1のプログラムを実行していまうと,/tmp/my_tmp_fileへデータを書き 込むことになるが,/tmp/my_tmp_file は /etc/passwd ファイルへのシンボリックリンクであるので,/etc/passwd ファイルへ書き込むことになる。実行しているのは root ユーザであるから,リスト1のプログラムは root 権 限で実行され,/etc/passwd ファイルへの書き込み権限を持つ。結果として /etc/passwd ファイルが壊れてしま うので,誰もこのホストへログインできなくなってしまう。 実行例3 シンボリックリンクアタック 1 $ ln -s /etc/passwd /tmp/my_tmp_file このセキュリティホールの要因は次の 2 点である。 ・テンポラリファイルのファイル名が既知であった ・既に同じ名前のファイル(シンボリックリンク)が存在することを確認していなかった テンポラリファイルを作成するときには,予測不可能なファイル名にすべきであり,もし同じ名前のファイ ルやシンボリックリンクが存在していた場合には,別のファイル名で作成すべきである。

1 $ perl -e 'for(;;){system("cat /tmp/my_tmp_file 2>/dev/null");}' ←プログラム

2 IPA IS A JAPANESE QUASI-GOVERNMENT 3 ORGANIZATION ESTABLISHED IN

4 DANCE WITH THE LAW FOR INFORMATION  ←読み取れた内容

5 PROCESSING TECHNOLOGY PROMOTION, 6 (LAW NO.90, MAY 22, 1979).

・・・以下,2 ∼ 6 行目の繰り返し・・・

(4)

テンポラリファイルの扱いで気をつけるべきこと

テンポラリファイルに関わるセキュリティホールの例をいくつか紹介した。以降ではこうしたセキュリティ ホールを作らないための対策について解説する。テンポラリファイルを作成するとき,以下のような点に気 をつけなければならない。 ・アクセス権限は自分のみに与える ・同名のファイル(やシンボリックリンク)が存在しないことを確認する ・ランダムで予測困難なファイル名を生成する ・他ユーザが削除できないようにする また,別のプログラムなどで処理したデータがテンポラリファイルとして与えられることがある。テンポラ リファイルをオープンするには,次の点に気をつける必要がある。 ・シンボリックリンクでないことを確認する 以下では上記注意点に対する対策方法をそれぞれ説明していく。

ファイルモードは 600 にしよう

リスト1では fopen( )関数によりテンポラリファイルを作成していた。この関数を使用した場合,ファイル モードは 666 として作成される。実際には更にプロセスの umask 値が適用され,作成されるテンポラリファ イルのファイルモードは644となることが一般的である。umaskはファイル作成時のデフォルトファイルモー ドを制御する,Unix プロセスの属性値である。fopen( )に関する詳細は参考文献『詳細 UNIX プログラミング

[新装版]』またはお手元のシステムの“man fopen”を参照していただきたい(man は Unix/Linux システムで

オンラインマニュアルを呼び出すためのコマンド)。 ファイルモードを 600 として作成することにより,テンポラリファイルにアクセスできるユーザは自分だけ に限定できる。ファイルモードを 600 と指定するためには,fopen( )関数ではなく,リスト2の open( )システ ムコールを使用する。1行目はopen( )システムコールを使ったテンポラリファイルの安全な作成方法である。 1 つ目の引数では作成するテンポラリファイルのパスを与えている。2 つ目の引数では新規ファイルを作成す る指示を与えている。この引数については次節で解説する。3 つ目の引数では作成するファイルのファイル モードで,マクロ定数 S_IRUSR と S_IWUSR の論理和 600 を指定している。ファイルモードのマクロ定数は 9 ∼ 17 行目のように定義されている。open( )システムコールの詳細についても参考文献『詳細 UNIX プログ ラミング[新装版]』または“man open”(オンラインマニュアル)を参照していただきたい。

ファイル作成は O_CREAT|O_EXCL で open( )しよう

テンポラリファイルを作成するときに,同名のファイル(やシンボリックリンク)が存在しないことを確認 すべきである。open( )システムコールの 2 つ目の引数に O_EXCL を与えることにより,これを確認できる。 2 つ目の引数による open( )システムコールの動作の違いをリスト3にまとめた。 1 行目の O_RDWR マクロのみの指定では,open( )システムコールは既存のファイルを読み書き両用でオープ ンする。もしファイルが存在していなければ open( )システムコールはエラーを返す。2 行目の指定では O_RDWR マクロにO_CREAT マクロが追加されている。この場合,既存のファイルがある場合,そのファイ ルを読み書き両用でオープンする。ファイルが存在しない場合,新規ファイルが作成される。一般的にこの 2 行目の指定がよく利用されているが,これだけではシンボリックリンクアタックなどに脆弱である。3 行目

(5)

がファイルを安全に作成する指定である。O_EXCL マクロは O_CREAT マクロと一緒に指定するマクロ定数 で,既存ファイルが存在しない場合,open( )システムコールがエラーを返すようになる。エラーが返ってくる ことにより,同名のファイルやシンボリックリンクが存在していることを知ることができる。言い換えると, O_EXCL マクロを指定しない場合,同名のファイルやシンボリックリンクが存在していたときに,それを知 る手段がない。 テンポラリファイルを作成するときには 3 行目の指定のように O_CREAT|O_EXCL を必ず指定すべきであ る。これにより,もし作成しようとしているファイル名で,既にファイルやシンボリックリンクが存在して いた場合,open( )システムコールはエラーを返してくれる。エラーを返してきた場合には,別の新しいファイ ル名で再挑戦すればよい。

作成したらすぐにファイルを削除しておこう

テンポラリファイルを一時的に使用するプログラムは,プログラム終了時にテンポラリファイルを削除する ことが多い。特に情報漏洩を気にするプログラムの場合は重要な情報を含んでいるテンポラリファイルを残 すべきではない。プログラムがテンポラリファイルの削除処理まで到達しないうちに異常終了してしまった 場合,テンポラリファイルが残ってしまう。 Unixファイルシステムにはちょっと便利な特性がある。リスト4を見ていただきたい。テンポラリファイル を使った簡単なプログラムである。注意すべき点は,20行目の open( )システムコールでテンポラリファイル を作成したにも関わらず,直後の 23 行目の unlink( )システムコールによりそのファイルを削除しているので ある。しかし以降の処理ではwrite( )システムコール,lseek( )システムコール,read( )システムコールが続いて おり,削除したはずのテンポラリファイルに対する処理が行われている。 実は Unix ファイルシステムでは,unlink( )によりファイルを削除しようとしても,そのファイルをオープン しているプロセスが存在すれば,すぐにファイルは削除されない。すべてのプロセスがクローズした時点で ファイルは削除されるのだ。これを利用して,open( )直後にunlink( )しておけば,プロセスが異常終了したと きにも,(OS がファイルをクローズしてくれるので)自動的にファイルが削除される。重要な情報を含むテ ンポラリファイルを扱うときに便利である。またunlink( )直後,ファイル自体は存在しつづけるのだが,どの 1 int fd = open("/tmp/my_tmp_file", O_CREAT|O_RDWR|O_EXCL, S_IRUSR|S_IWUSR);

2 3 O_CREAT  ←ファイル作成 4 O_RDONLY  ←fdを読み取り専用としてオープン 5 O_WRONLY  ←fdを書き込み専用としてオープン 6 O_RDWR  ←fdを読み書き両方としてオープン 7 O_EXCL  ←O_CREATと合わせて使ったとき,ファイルが既存ならエラー 8 9 S_IRUSR 0400 ←所有者の読み取り許可 10 S_IWUSR 0200 ←所有者の書き込み許可 11 S_IXUSR 0100 ←所有者の実行許可 12 S_IRGRP 0040 ←グループの読み取り許可 13 S_IWGRP 0020 ←グループの書き込み許可 14 S_IXGRP 0010 ←グループの実行許可 15 S_IROTH 0004 ←他人の読み取り許可 16 S_IWOTH 0002 ←他人の書き込み許可 17 S_IXOTH 0001 ←他人の実行許可 リスト2 テンポラリファイルを open()システムコールで作成する 1 O_RDWR  ←既存のファイルをオープン,存在しなければエラー 2 O_RDWR | O_CREAT  ←既存のファイルをオープン,存在しなければ新規作成

3 O_RDWR | O_CREAT | O_EXCL ←新規ファイルを作成しオープン,既存であればエラー

(6)

ディレクトリにも属さない状態となる。つまりファイル名が存在しないファイルとなる。よってこのファイ ルは二度と open( )できなくなる。unlink( )以降はそのファイルへアクセスできるのは,unlink( )以前に open( ) したプロセスだけとなる。他のプロセスからアクセスさせないという使い方でも便利である。 リスト4を実行した例を実行例4に示す。5∼ 10行目が示すとおり,unlink( )後のテンポラリファイルに書き 込んだメッセージを,ちゃんと読み込むことができている。11 行目以降で /tmp ディレクトリに my_tmp_file が残っていないことも確認できる。更に 3 行目と 13 行目を比較することにより,/tmp ディレクトリのタイム スタンプが変化していることも確認できる。これは /tmp/my_tmp_fileが一時的に存在していたことを示してい る。 1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <string.h> 7

8 #define TMPFILE "/tmp/my_tmp_file" ←テンポラリファイル名

9 #define MSGTEXT "IPA is a Japanese quasi-government\n" \ 10 "organization established in accor-\n" \ 11 "dance with The Law for Information\n" \ 12 "Processing Technology Promotion,\n" \ 13 "(Law No.90, May 22, 1979).\n"

14 15 int main(void) 16 { 17 char readbuf[1024]; 18 int fd; 19

20 fd = open(TMPFILE, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); ←テンポラリファイル作成

21 if(fd==-1) exit(1); 22

23 unlink(TMPFILE); ←オープンしたまま削除

24

25 if(write(fd, MSGTEXT, sizeof(MSGTEXT))==-1) exit(1); ←削除しても書き込み可能

26

27 if(lseek(fd, 0, SEEK_SET)==-1) exit(1); ←削除してもシーク可能

28

29 memset(readbuf, 0, sizeof(readbuf));

30 if(read(fd, readbuf, sizeof(readbuf)-1)==-1) exit(1); ←削除しても読み込み可能

31 32 printf("%s", readbuf); ←読み込み結果を表示 33 34 close(fd); ←クローズ 35 return 0; 36 }

リスト4 pre_unlink.c open()直後に unlink()してしまうという Unix ならではの技

1 $ ls -la /tmp 2 total 8

3 drwxrwxrwt 2 root root 4096 Dec 9 05:02 . 4 drwxr-xr-x 17 root root 4096 Aug 6 11:30 .. 5 $ ./pre_unlink

6 IPA is a Japanese quasi-government

7 organization established in accor- ←unlink()したのに,

8 dance with The Law for Information  read()できている

9 Processing Technology Promotion, 10 (Law No.90, May 22, 1979). 11 $ ls -la /tmp

12 total 8

13 drwxrwxrwt 2 root root 4096 Dec 9 05:03 .  ←タイムスタンプが変化

14 drwxr-xr-x 17 root root 4096 Aug 6 11:30 ..

↑my_tmp_file は存在しない

(7)

予測できないファイル名にしよう

固定のファイル名でテンポラリファイルを作成していると,悪意あるユーザによって同名のシンボリックリ ンクなどを予め仕掛けられてしまう。作成するテンポラリファイルのファイル名は毎回ランダムに変化さ せ,ファイル名の予測を困難にしよう。これにより悪意あるユーザが同名のファイルやシンボリックリンク を仕掛けるのが困難になる。 ランダムに決定したファイル名でテンポラリファイルを作成するとき,O_CREAT|O_EXCL を指定して open( ) するのを忘れてはいけない。万一,悪意あるユーザがファイル名を予測してシンボリックリンクなどを仕掛 けていた場合でも,これを検知できる。もし同名のファイルやシンボリックリンクが既に存在していたなら ば,別のファイル名をランダムに生成して再挑戦すればよい。O_CREAT|O_EXCL を指定した open( )でファ イル作成が成功するまで繰り返せばよい。 ランダムで予測不可能なファイル名の生成アルゴリズムは込み入った話になるので,ここでは紹介しない。 後述するが mkstemp( )関数や tmpfile( )関数を使うことで,安全で適切なテンポラリファイルの作成が可能で ある。これらの関数は以下の 3 つの注意点をすべて網羅している。 ・アクセス権限は自分のみに与える ・同名のファイル(やシンボリックリンク)が存在しないことを確認する ・ランダムで予測困難なファイル名を生成する

ディレクトリにはセーブテキストビットを立てよう

実行例5のような Unixファイルシステムの特性をご存知であろうか。意外と知らない方も多いようである。 7 行目の root が所有するファイル my_secret_file は一般ユーザ foo から読み書きするのは不可能である。しか し削除できてしまうのである。8行目で rm コマンドでmy_secret_fileを削除しようとしているが,これを実行 しているのは一般ユーザ foo である。9 行目で確認メッセージが出てくるが,yes と入力している。10 ∼ 13 行 目の ls コマンドの表示から,root が所有していたファイル my_secret_file が削除されてしまっていることが分 かる。 実は,ファイルの読み書き権限はそのファイルのファイルモードにより決定するが,ファイルの削除権限は そのファイルの親ディレクトリのファイルモードにより決定する。親ディレクトリのファイルモードで書き 込みが許可されているユーザは,そのディレクトリ内のファイル削除が許可される。5行目から my_secret_file 1 $ id

2 uid=411(foo) gid=411(bar) groups=411(bar) ←一般ユーザ(foo)である

3 $ ls -la 4 total 8

5 drwxrwxr-x 2 foo bar 4096 Dec 5 18:45 . 6 drwxrwxr-x 9 foo bar 4096 Dec 5 18:44 ..

7 -rw--- 1 root root 10 Dec 5 18:45 my_secret_file

↑root 以外読み書き権限なし

8 $ rm my_secret_file ←root 所有のファイルを削除

9 rm: remove write-protected file `my_secret_file'? yes 10 $ ls -la

11 total 8 ↓削除できてしまった

12 drwxrwxr-x 2 foo bar 4096 Dec 5 18:47 . 13 drwxrwxr-x 9 foo bar 4096 Dec 5 18:44 .. 14 $

(8)

の親ディレクトリの所有者は foo であり,所有者 foo(とグループ bar)はそのディレクトリに対し読み書き が許可されている。したがって root 所有の my_secret_file は一般ユーザ foo から削除できてしまう。

何故このようなことを説明するかというと,/tmp ディレクトリはすべてのユーザが書き込み権限を持つから である。たとえ root 所有のテンポラリファイルでさえ,一般ユーザが削除できてしまう・・・ということが考 えられるのである。実は過去の Unix システムでは実際に一般ユーザで root所有のテンポラリファイルを削除 できてしまった。 現在の Unix システムの /tmp ディレクトリはこの問題を解決している。ls コマンドで /tmp ディレクトリを見 てみると実行例6のようなファイルモードとなっている。2 行目の other ユーザの実行許可部分に普段見かけ ない文字「t」がある。これはディレクトリのセーブテキストビットが on になっていることを示している。 ディレクトリのセーブテキストビットがon の場合,そのディレクトリ内のファイルの削除権限の扱いがこれ までの説明とは異なる。以下のいずれかの条件に合致しないとファイルの削除権限が与えられない。 ・ファイルの所有者であること ・ディレクトリの所有者であること ・root であること セーブテキストビットの詳細については参考文献『詳細 UNIX プログラミング[新装版]』を参照していただ きたい。

デーモン専用のテンポラリファイルディレクトリを用意しよう

Unix システムはセーブテキストビットの導入により,不特定多数のユーザに書き込み権限のある /tmpディレ クトリにおけるファイルの削除権限の扱いがようやく安全になった。しかし本質的には「不特定多数のユー ザに書き込み権限がある」から問題が生じるのである。 ネットワークサービスなどのようなデーモンプロセスは専用のユーザを割り当てて,そのユーザ権限で実行 させることが多い。デーモンプロセス専用のテンポラリファイルディレクトリを作成し,そのデーモンだけ しかアクセスできないようにしてしまえば,/tmpディレクトリのような削除権限問題は起きない。もちろん, デーモン専用テンポラリファイルディレクトリの所有者はそのデーモンプロセスを実行する専用ユーザとな るように設定した上で,そのディレクトリへの読み書き権限はその専用ユーザにしか与えない。この場合, セーブテキストビットは不要である。 デーモンプロセスの場合,専用テンポラリディレクトリを準備したほうが安全でしかもシンプルである。し かし不特定多数のユーザが実行するようなコマンドの場合,専用ディレクトリを用意するのは難しい。そう いった場合にはセーブテキストビットが onになっている/tmpディレクトリにテンポラリファイルを作成した ほうがよいだろう。専用テンポラリディレクトリを使うか,/tmp ディレクトリを使うかは,作成するプログ ラムに応じて適宜使い分けていただきたい。 1 $ ls -ld /tmp

2 drwxrwxrwt 5 root root 4096 Dec 5 04:24 /tmp

t:

セーブテキストビット

(9)

シンボリックリンクでないことを確認しよう

別のプログラムなどで処理したデータがテンポラリファイルとして与えられることがある。既存のテンポラ リファイルを安全にオープンするには,それがシンボリックリンクでないことを確認する必要がある。シン ボリックリンクかどうかを確認するには lstat( )システムコールをリスト5のように使用する。 実際にはレースコンディション対策も加味する必要があり,安全なテンポラリファイルのオープン方法はリ スト7の safeopen( )関数(後述)のようになる。

具体的な対策要領

以上,テンポラリファイルを扱う上でのいくつもの興味深い注意点を紹介してきた。実はこれらの問題の殆 どは以降で説明する 4 つの対策要領でカバーできる。

安全なテンポラリファイルの作成方法

次の 2 つの関数を使用することで,安全にテンポラリファイルを作成できる。 ・int mkstemp(char *template);

・FILE *tmpfile(void); mkstemp( )関数はテンポラリファイルを作成,オープンし,ファイル記述子を返す。mkstemp( )関数の引数に は作成するテンポラリファイルのファイル名のテンプレートを渡す。テンプレートは /tmp/my_tmp_file_XXXXXX といった文字列で,文字列の最後は 6 つの「X」で終わる必要がある。この「XXXXXX」部分が自動的にラ ンダムな文字列に置き換えられ,ランダムな名前のテンポラリファイルが作成される。テンプレートに /mydaemon_dir/tmp/prefix_XXXXXX といった文字列を与えると,デーモン専用のテンポラリファイルディレクトリ「/mydaemon_dir/tmp/」にテン ポラリファイルを作成することもできる。 mkstemp( )関数を使用する上で,注意すべき点が 1 つある。テンプレート文字列は,書き込み可能なバッファ 上になければならない。mkstemp( )関数が「XXXXXX」の部分を書き換えるからである。リスト6に mkstemp ( )関数の使い方を示す。1 ∼ 3 行目にテンプレート filepath の初期化方法を 3 つ示す。1 行目,2 行目の初期化 方法ではfilepathはスタック上にバッファが作られ,そこに文字列「/tmp/my_file_XXXXXX」が書き込まれる。 スタック上のバッファであるので mkstemp( )関数が「XXXXXX」部分を上書き可能である。一方 3 行目の初 1 struct stat lst; 2

3 lstat("/tmp/my_tmp_file", &lst) ←stat 構造体に情報を取得

4 if (!S_ISREG(lst.st_mode)) { ←シンボリックリンクかどうかを確認

5 シンボリックリンクの場合の処理

6 }

(10)

期化方法では,文字列「/tmp/my_file_XXXXXX」はコンパイル時に固定文字列領域に配置され,filepath 変数 はその固定文字列領域を指すポインタとなる。したがって mkstemp( )関数は「XXXXXX」が固定文字列領域 にあるため上書きできない。実際に実行すると Segmentation fault が発生する。 tmpfile( )関数はmkstemp( )関数を使いやすくした位置付けのものである。具体的には次のような動作をする。 ・mkstemp( ) 関数により /tmp ディレクトリ下にテンポラリファイルを作成 ・unlink( ) システムコールによりファイル削除 ・fdopen( ) 関数により FILE* を取得 テンプレートを渡す必要はなく,/tmp または /var/tmp ディレクトリ下にテンポラリファイルを作成してくれ る。unlink( )システムコールを呼んでくれるので,ファイルは自動的に削除される。ファイル記述子ではなく FILE* を返してくれるので,標準入出力関数群を使える。このように tmpfile( )関数はテンポラリファイルの 扱いを簡単にしてくれる。ただしテンポラリファイルを削除したくない場合は,mkstemp( )関数を使うべきで ある。

mkstemp( )関数も tmpfile( )関数もテンポラリファイル作成のファイルモードは600である。ただし古めの Unix システム(例えば glibc2.0.6 以前を使用するもの)は 666 となっており,こうした古めのシステム上での動作 も考える場合は,umask( )システムコールとの併用も必要である。

安全なテンポラリファイルのオープン方法

リスト7のsafeopen( )関数を使用することにより,既に存在するテンポラリファイルを安全にオープンするこ とができる。リスト7の safeopen( )関数は指定ファイルをシンボリックリンクかどうかを確認してオープン する関数である。シンボリックリンクであった場合,エラーを返す。シンボリックリンクを確認するために, lstat( )システムコール(6 行目)によりファイル情報を取得し,8 行目でシンボリックリンクかどうかを確認 している。lstat( )システムコールへシンボリックリンクのパスを与えると,シンボリックリンクそのものの ファイル情報を取得してくれる。 13 ∼ 22 行目の fstat( )システムコール以降はレースコンディション対策である。詳細については関連記事 『7-5. Unix のレースコンディション』を参照していただきたい。

1 char filepath[256] = "/tmp/my_file_XXXXXX"; ←(正しい)スタック上に配置

2 char filepath[] = "/tmp/my_file_XXXXXX"; ←(正しい)スタック上に配置

3 char *filepath = "/tmp/my_file_XXXXXX"; ←(間違い)固定文字列領域に配置

↑6 文字の 'X' が必要

4 fd = mkstemp(filepath);

5 filepath → "/tmp/my_file_PvQLMk" ←ランダムな文字列に変更される

^^^^^^

(11)

適切なテンポラリファイルディレクトリの設定

適切なテンポラリファイルディレクトリの設定は,そのディレクトリの使用方法によって異なる。 グローバルテンポラリファイルディレクトリ グローバルテンポラリファイルディレクトリは /tmp や /var/tmp のように不特定多数のユーザへの読み書き 許可を与えているディレクトリである。この場合,ディレクトリの設定は次を満たすことにより適切な設 定となる。 ・ファイルモードは 777 ・セーブテキストビットは on 専用テンポラリファイルディレクトリ 専用テンポラリファイルディレクトリはネットワークサービスなどのデーモンプロセスの専用テンポラリ ファイルディレクトリである。この場合,ディレクトリの設定は次を満たすことにより適切な設定となる。 ・ファイルモードは 700 ・所有者はデーモン専用のユーザ

使ってはいけない関数

次の 3 つのテンポラリファイルを扱う関数は使ってはならない。これらの関数は古い時代から存在するもの だが,不適切な関数インタフェースや内部実装の問題により,多くのセキュリティホールの原因となってき た。mkstemp( )関数とtmpfile( )関数はこれらの反省にしたがい,関数インタフェース及び内部実装を改良した ものである。 ・tmpnam( ) マルチスレッド環境で使用不可能 ・tempnam( ) マルチスレッド環境で使用不可能 ・mktemp( ) 生成するファイル名が推測可能 1 int safeopen(char *filepath)

2 {

3 struct stat lst, fst; 4 int fd;

5

6 if (lstat(filepath, &lst) != 0) return -1; 7 8 if (!S_ISREG(lst.st_mode)) return -1; ←シンボリックリンク,エラー終了 9 10 fd = open(filepath, O_RDWR, 0); 11 if (fd < 0) return -1; ←オープン失敗,エラー終了 12 13 if (fstat(fd, &fst) != 0) { 14 close(fd); ←fstat()に失敗したので, 15 return -1; fd をクローズしてエラー終了 16 } 17 18 /* lstat()とfstat()のデバイス番号とiノード番号が一致するか調べる */

19 if (lst.st_ino != fst.st_ino || lst.st_dev != fst.st_dev) {

20 close(fd); ←一致していないので, 21 return -1;  エラー終了 22 } 23 24 return fd; 25 } リスト7 安全なファイルのオープン方法

(12)

まとめ

共通のディレクトリに作られるテンポラリファイルは他者からの干渉を受けやすく,セキュリティ脆弱性を 生みやすい。シンボリックリンクによる「罠」などは有名な手口である。テンポラリファイルの安全を守る ためには,専用のディレクトリ,予測困難なファイル名,厳重なアクセス権の選定といった対策がある。 mkstemp( )関数や tmpfile( )関数を利用するのも有効な手段の一つである。

関連記事

『7-5. Unix のレースコンディション』

参考文献

『詳細 UNIX プログラミング[新装版]』,W・リチャード・スティーヴンス,大木 敦雄訳,2000 年,株式会社 ピアソン・エデュケーション

“man fopen”,“man open”,“man mkstemp”,“man tmpfile”

(Unix / Linux システムのオンラインマニュアル)

『2.3 Insecure Use of Temporary Files』(英文)

http://www.securityfocus.com/popups/forums/secprog/secure-programming.shtml#2.3

『2.4 File Race Conditions』(英文)

参照

関連したドキュメント

うのも、それは現物を直接に示すことによってしか説明できないタイプの概念である上に、その現物というのが、

これはつまり十進法ではなく、一進法を用いて自然数を表記するということである。とは いえ数が大きくなると見にくくなるので、.. 0, 1,

共通点が多い 2 。そのようなことを考えあわせ ると、リードの因果論は結局、・ヒュームの因果

人の生涯を助ける。だからすべてこれを「貨物」という。また貨幣というのは、三種類の銭があ

自然言語というのは、生得 な文法 があるということです。 生まれつき に、人 に わっている 力を って乳幼児が獲得できる言語だという え です。 語の それ自 も、 から

3月 がつ を迎え むか 、昨年 さくねん の 4月 がつ 頃 ころ に比べる くら と食べる た 量 りょう も増え ふ 、心 こころ も体 からだ も大きく おお 成長 せいちょう

これも、行政にしかできないようなことではあるかと思うのですが、公共インフラに

を負担すべきものとされている。 しかしこの態度は,ストラスプール協定が 採用しなかったところである。