アーカイブ

第7章 1.最小の特権

公開日:2007年9月26日

独立行政法人情報処理推進機構
セキュリティセンター

本ページの情報は2007年9月時点のものです。
記載の資料は資料公開当時のもので、現在は公開されていないものも含みます。

特権が狙われる

特権をもつプログラムが侵害された場合、システム全体が乗っ取られるなど、深刻な被害が発生するおそれがある。プログラムにはなるべく特権を与えないようにするか、必要最小限のもののみ与える配慮が必要である。

多くのプラットフォームでは、システムの設定を変更したり重要なリソースにアクセスすることを特権をもつプログラムのみに許す、というやり方をとることが多い。これは、一般のプログラムがそれらのリソースに勝手にアクセスするのを防ぐのが目的である。

攻撃者の観点からすると、特権が与えられたプログラムはシステムの設定を変更でき重要なリソースを手中に収めることのできる、システム侵害の格好の標的である。

特権を表すアカウント

「システムのシャットダウンの特権」のように、特定の特権の概念が組み込まれていて、それらを特定のアカウントグループに与えるという仕組みをもつWindowsのようなプラットフォームがある。

しかしそのWindowsも含め、多くの場合管理者アカウント(root、Administrator等)やシステムアカウント(SYSTEM等)で動作させることによってプログラムに特権を与えるという方式がとられることが多い。

特権が必要なプログラムの考慮

ネットワークに向けてサービスを提供する「デーモン」や「サービス」と呼ばれるシステム常駐型のサーバプログラムは、何らかの特権を与えられて動作していることがしばしばである。

その裏側には、これらのサーバプログラムが動作に必要とするシステムリソースの利用には特権が必要であるといった事情がある。プログラムによっては、特権とまったく無関係に動作させることは難しい。

そうした特権を利用するプログラムにおいても、次のような工夫をすることによって、攻撃者が侵害に成功した際にそれらの特権を不正に行使するのを防ぐことができる。

プログラム分割

特権を必要とする処理を独立した小さなプログラムにして本体から切り離す。本体と特権プログラムの間はプロセス間通信等の手段を用いて連携する。

特権の一時的放棄

特権を必要としない処理を行うあいだ一時的に特権を手放す。特権の放棄には、プログラムの動作アカウントを切り替えるAPIを利用する。

  • 図24: 特権の切り離し

Unix系、GNU/Lnux系プラットフォームとroot権限

Unix系、GNU/Linux系のプラットフォームにおいては、例えば、次のことを行うのにroot権限が必要である。

  • bind()関数を用いてTCP/IPの1023およびそれ以下のポート番号でリクエストを待ち受ける
  • setrlimit()関数を用いてシステムのリソースリミットを増やす
  • chroot()関数やjail()関数を呼び出す
  • manのセクション8に属する諸々のシステム管理コマンドを呼び出す 等

また、ユーザアカウント情報等rootが管理する重要ファイルへのアクセスが必要な場合、デーモンにroot権限を与えることが行われる。

Unix系、GNU/Lnux系プラットフォームのアカウント切り替え関数

Unix系、GNU/Lnux系プラットフォームのプログラムは、次の関数の使用によって一時的あるいは恒久的に動作アカウントを別のものに切り替えることができる。

  • seteuid()関数 実効ユーザIDの一時的変更
  • setuid()関数 ユーザIDの恒久的変更(rootが呼び出した場合)

(1) seteuid()関数を用いた一時的特権放棄

seteuid()関数は、実効ユーザIDを変更する関数である。Unix系、GNU/Linux系プラットフォームにおいては、プログラム動作時のアカウントの情報として「保存ユーザID」「実ユーザID」「実効ユーザID」の3つが保持されている。このうち実効ユーザIDは「実質的に効果をおよぼすユーザID」のことである。保存ユーザID、実ユーザIDの値がどのようなものであっても、プログラムは実効ユーザIDのアカウントの権限で動作する。次は、seteuid()関数を用いて実効ユーザIDを変更する例である。

[例]

uid = USERID;       // 管理者でないユーザID(0以外)
error = seteuid(uid);   // 管理者権限の一時的な放棄

seteuid()関数による実効ユーザIDの変更の場合、以前rootアカウントで動作していたプログラムは、もう一度seteuid()関数を呼び出すことで再びrootアカウントに復帰できる。

[例]

uid = 0;         // rootのユーザID
error = sesteuid(uid);  // 管理者権限への復帰

(2) setuid()関数を用いた恒久的特権放棄

setuid()関数は、rootアカウントから呼び出した場合、恒久的にユーザIDを変更する関数としてはたらく。rootアカウントで動作するプログラムがsetuid()関数を呼び出してroot以外のアカウントへの変更を行うと、再びrootアカウントに戻ることはできない。

[例]

uid = USERID;      // 管理者でないユーザID(0以外)
error = setuid(uid);   // 管理者権限の恒久的な放棄

まとめ

システムの濫用を防ぐために設けられた特権のしくみは、権限の集中をもたらすため、攻撃者の標的になり得る。特権が不可欠なプログラムであっても、それを短時間しか保有しないことが重要である。