Silverlight をインストールするには、ここをクリックします*
Japan変更|すべてのMicrosoft のサイト
MSDN
|MSDN ライブラリ|デベロッパー センター|ダウンロード情報|開発ツール製品|コミュニティ|ご意見・ご要望|サイトマップ
MSDN Home > 連載コラム > Code Secure > 2 つの目立たないセキュリティの問題に取り組む

2 つの目立たないセキュリティの問題に取り組む

Michael Howard
Secure Windows Initiative

August 14, 2002
日本語版最終更新日 2002 年 10 月 4 日

最近よく見かけるいくつかの誤りを説明し、 それを人々に知らせることに価値があります。 今月の記事では、以下の 2 つのトピックについて説明しようと思います。

  1. 対話型サービス
  2. _alloca() の呼び出し

セキュリティ、サービス、および対話型デスクトップ

Unix のデーモンと同様に、 サービスは Microsoft Windows NT® の中核であり、ユーザーとの対話を必要としないオペレーティング システムやユーザーに重要な機能を提供します。サービスを作成する場合に、認識しておく必要のある問題点がいくつかあります。

一般的に、Microsoft Windows® のサービスは、ユーザー インターフェイスを持たず、無人で実行するように設計されたコンソール アプリケーションです。しかしながら、場合によっては、サービスがユーザーとの対話を必要とすることもあります。 SYSTEM など、高い権限を持つセキュリティ コンテキストで実行されるサービスは、対話型のサービスとして実行しないほうがよいです。Windows のユーザー インターフェイスでは、デスクトップがセキュリティの境界になります。対話型デスクトップ上で実行されるどんなのアプリケーションでも、対話型デスクトップ上のすべてのウィンドウと対話できます。たとえ、非表示のウィンドウであってもです。このことは、ウィンドウを作成するアプリケーションのセキュリティ コンテキスト、およびアプリケーションのセキュリティ コンテキストに関係なく適用されます。

このようなデザイン上の特質により、対話型デスクトップ上でウィンドウを開くすべてのサービスは、ログオンしているユーザーにより実行しているアプリケーションに対してそのサービス自体を露呈することになります。サービスが機能を制御するためにウィンドウ メッセージの使用を試みるとすると、ログインしているユーザーは、悪意あるメッセージを通して、その機能を混乱させることができます。

SYSTEM として実行するサービス、 つまり OpenWindowStationGetThreadDesktop への呼び出しを使って対話型デスクトップをサポートとするようなサービスはお勧めできません。今後リリースされるバージョンの Windows では、対話型サービスのサポートを完全に取り除く可能性があることに注意してください。

サービスの作成者は、クライアント/サーバー テクノロジ (RPC、ソケット、名前付きパイプ、または COM など) を使用して、サービスからログオンしているユーザーとの対話を行い、また、簡単なステータスを表示するために MB_SERVICE_NOTIFICATION を指定した MessageBox を使用することをお勧めします。サービス コードが以下の性質を持っている場合は、注意が必要です。

  • LocalSystem として実行され、サービスが セキュリティ構成マネージャの [ログオン] で [デスクトップとの対話をサービスに許可] に設定されているか、 またはレジストリ キーが HKLM\CCS\Services\MyService\Type & 0x0100 == 0x0100 に設定されている場合
  • CreateService および dwServiceType & SERVICE_INTERACTIVE_PROCESS == SERVICE_INTERACTIVE_PROCESS
  • uType と (MB_DEFAULT_DESKTOP_ONLY | MB_SERVICE_NOTIFICATION | MB_SERVICE_NOTIFICATION_NT3X) != 0 を指定して、 MessageBox() を呼び出す場合
  • OpenDesktop("winsta0",...) を呼び出して、 そのデスクトップ上に UI を作成する場合
  • OpenDesktop の LoadLibrary/GetProcAddress を使用している場合

_alloca に注意

_alloca 関数は、 スタック上に動的メモリを割り当てます。 割り当てられた領域は、 その割り当てがスコープ外になったときではなく、 呼び出し元の関数が終了するときに、自動的に解放されます。 次に _alloca を使用するサンプル コードを示します。

void function(char *szData) {
    PVOID p = _alloca(lstrlen(szData));
   // p を使用します
}

攻撃者がスタックのサイズよりも長い szData を渡すと、 _alloca が例外を発生し、 アプリケーションは停止します。 このような処理は、特にコードがサーバーで実行されている場合は望ましくありません。 このようなエラー状況に対処する適切な方法は、例外ハンドラで _alloca の呼び出しをラップし、エラーが発生したときにスタックをリセットすることです。

void function(char *szData) {
    __try {
        PVOID p = _alloca(lstrlen(szData));
        // p を使用します
    } __except ((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ?
                   EXCEPTION_EXECUTE_HANDLER :
                   EXCEPTION_CONTINUE_SEARCH) {
        _resetstkoflw();
    } 
}

関連問題 : ATL 変換マクロ

_alloca を呼び出す特定の ATL 文字列変換マクロについても注意する必要があります。 これらのマクロには、A2WW2ACW2CT などがあります。 コードがサーバー コードの場合、データ長を考慮せずにこれらの変換関数を呼び出さないほうがよい。これは、入力されたものを信頼しないという簡単な一例です。攻撃者がコードに 10 MB の文字列を渡した場合、スタックが破壊され、例外が発生するでしょう。そして、例外がキャッチされないと、途方に暮れることになるでしょう。ですから、このような処理は行わないでください !

欠陥を発見する

先週のコードの欠陥を見つけられた人はいませんでした。 ただし、数人の方はあと一歩のところまで来ていました。 このコードで問題なのは、 平文と暗号文に同じバッファを使用していることです。 これは行ってはならない処理です。

平文を格納し、 その後同じバッファを使用して暗号文を生成するためにその平文を暗号化することには一見害がないように見えるかもしれません。 多くの場合、このような処理を行っても害はありません。 ただし、マルチスレッド環境では、状況が異なります。 たとえば、コードで "競合状態" が発生しているときに、 それに気付かないとします (競合状態は、ソフトウェアのイベントの相対的なタイミングに関する予期しない重要な依存関係によって発生します。 通常、競合状態は同期エラーによって発生します。) 率直に言うと、深刻な競合状態が発生していることに気付いたときは、 既に手遅れです。 たとえば、アプリケーションの通常の処理が次のように行われるとしましょう。

  1. 平文をバッファに読み込みます。
  2. バッファを暗号化します。
  3. バッファの中身を受信者に送信します。

この処理には問題がないように見えます。しかしながら、マルチスレッド アプリケーションであった場合に、何らかの理由により、後ろ 2 ステージが競合状態により、スワップすると想像してみます。

  1. 平文をバッファに読み込みます。
  2. バッファの中身を受信者に送信します。
  3. バッファを暗号化します。

受信者は、平文だけを受信します ! この問題点は Internet Information Server 4.0 で修正されたバグでした。極端に負荷が高く、また稀な条件の場合に、サーバーからユーザーへのデータ通信を保護するための SSL (Secure Sockets Layer) を使用しているときに、サーバーがこの形式に従っても、暗号化されていないデータのパケットをユーザーに送信することがありました。被害の可能性は小さいものでした。ユーザー (場合によっては、攻撃者) には 1 つのパケットだけが送信されます。それから、ユーザーがパケットを受信すると、クライアント ソフトウェアは接続を切断します。このように、この問題は Microsoft により修正されました。 脆弱性の詳細については、 「マルチスレッド SSL ISAPI フィルタ」の脆弱性に対する対策 (MS99-053) を参照してください。

この問題を解決するには 2 つのバッファを使用します。 一方のバッファは平文用、もう一方のバッファは暗号文用に使用します。 念のため暗号文のバッファは呼び出しごとにクリアするようにしました。

次のコードの欠陥を見つけることができるでしょうか ?

void ShuffleAndUpdate(char *szName, char *szPwd, 
                DWORD index,
                DWORD d) {
   DWORD dwArray[32]; 
   ZeroMemory(dwArray,sizeof(dwArray));
   BOOL fAllowAccess = FALSE;
   if (IsValidUser(szName,szPwd)) {
      fAllowAccess = TRUE;
      ShuffleArray(dwArray,szName);
         }
   dwArray[index]= d;
   if (fAllowAccess) {
      // 重要な処理を行います
   }
}

Michael Howard は、 Microsoft の Secure Windows Initiative グループの Security Program Manager であり、 「プログラマのためのセキュリティ対策テクニック (原題 Writing Secure Code)」の共著者でもあります。 また、「Designing Secure Web-based for Applications for Windows 2000」の著者です。 彼の人生における興味は、 人々がセキュアなシステムの設計、構築、テストおよびドキュメント化を行えるように手助けをすることにあります。 お気に入りの台詞は、「ある人にとっての機能は、別の人にとっての悪用の手段となる」というものです。




Microsoft