JPCERT コーディネーションセンター

POS02-C. 最小権限の原則に従う

POS02-C. 最小権限の原則に従う

最小権限の原則とは、システムのすべてのプログラムおよびユーザが、割り当てられている作業を遂行するために必要な最低限の権限で操作を行うことである [Saltzer 74、Saltzer 75]。詳細な定義については「Build Security In」の Web サイト [DHS 06] を参照のこと。実行時に与える権限を最小限にすることで、コードに脆弱性が発見された場合に悪用の被害を軽減できる。

違反コード

プログラム内で特権を要求される操作が必要な場合もあるが、そのようなプログラムも特権を保持し続ける必要があるとは限らない。たとえば、ネットワークプログラムの場合、生のネットワークパケットを捕捉するためにスーパーユーザ権限が必要であっても、それ以外の処理(パケット分析など)には同じ権限セットは必要でないことがある。それゆえ、必要に応じて権限の引き下げ、引き上げを行うような設計方針をとることが賢明である。また、必要な権限のみを割り当てることで、権限昇格を悪用する攻撃が成功する機会も限られる。

たとえば、既知のポート(ポート番号 1024 未満)を使う必要がある独自のサービスがあるとする。クライアントとの接続が悪意ある者に乗っ取られるのを防ぐため、カーネルは、スーパーユーザだけがシステムコール bind() を使ってこれらのポートに接続できるという条件を課している。

以下のコード例は、setuid によりスーパーユーザ権限で動作するよう設定されている。bind() を呼び出した後に、子プロセスを生成して登録処理を行っている。bind() 操作を実行した後も、このプログラムはスーパーユーザ権限で実行し続ける。

int establish(void) {
  struct sockaddr_in sa; /* リスニングソケットのアドレス */
  int s; /* リスニングソケット */

  /*  構造体にアドレスとポート番号を設定する  */

  sa.sin_port = htons(portnum);

  /*  その他、socket() などのシステムコール  */

  if (bind(s, (struct sockaddr *)&sa,
        sizeof(struct sockaddr_in)) < 0) {
    /* 後処理を実行する */
  }

  /* 呼び出し元に戻る */
}

int main(void) {
   int s = establish();

  /*  クライアントからの接続があるまで accept() でブロックする  */

   switch (fork()) {
      case -1 :  /* エラー。後処理して終了する */
      case  0 :  /* これは子プロセス。クライアントの要求に対応する */
      default :  /* これは親プロセス。ブロックを続行する */
   }
}

このプログラムの本体において、脆弱性を悪用して任意のコードを実行する攻撃が行われると、この悪意あるコードは、スーパーユーザ権限で実行される。

適合コード

プログラムは最小権限の原則に従い、ポートへの接続処理とその後の処理を慎重に区別すべきである。プログラム内の欠陥によってスーパーユーザのアカウントが侵害される可能性を最小限に抑えるには、特権付き操作の実行を終了した後、できるだけ早くスーパーユーザ権限を放棄すべきである。以下のコードでは、bind() 操作の実行直後に、権限を恒久的に引き下げている。また、「POS37-C. 権限の破棄は確実に行う」のとおり、特権を再度獲得できないことを確認している。

/*  特権で実行されるコード  */

int establish(void) {
  struct sockaddr_in sa; /* リスニングソケットのアドレス */
  int s; /* リスニングソケット */

  /* 構造体にアドレスとポート番号を設定する */

  sa.sin_port = htons(portnum);

  /* その他、socket() などのシステムコール */

  if (bind(s, (struct sockaddr *)&sa,
        sizeof(struct sockaddr_in)) < 0) {
    /* 後処理を実行する */
  }

  /* 呼び出し元に戻る */
}

int main(void) {
  int s = establish();

  /* 権限を恒久的に引き下げる */
  if (setuid(getuid()) == -1) {
     /*  エラー処理を行う  */
  }

  if (setuid(0) != -1) {
    /* 特権を復元可能。エラー処理を行う */
  }

  /* クライアントからの接続があるまで accept() でブロックする */

  switch (fork()) {
     case -1: /* エラー。後処理して終了する */
     case  0: /* 開いているファイル記述子をすべて閉じる
               * これは子プロセス。クライアントの要求に対応する
               */
     default: /* これは親プロセス。ブロックを続行する */
  }
}
リスク評価

最小権限の原則に従わないと、特権でのコード実行を許してしまうことがある。

レコメンデーション 深刻度 可能性 修正コスト 優先度 レベル
POS02-C P9 L2
参考情報
翻訳元

これは以下のページを翻訳したものです。

POS02-C. Follow the principle of least privilege

Top へ

Topへ
最新情報(RSSメーリングリストTwitter