Share via


セキュリティ保護された ASP.NET アプリケーションの構築 : 認証、認定、および通信のセキュリティ保護 第 12 章 データ アクセス セキュリティ

patterns and practices home

J.D. Meier, Alex Mackman, Michael Dunner, and Srinath Vasireddy
Microsoft Corporation

November 2002
日本語版最終更新日 2003 年 3 月 17 日

適用対象:
    Microsoft® ASP.NET
    Microsoft® SQL Server™

全体の概要については、「セキュリティ保護された ASP.NET アプリケーションの構築」の開始ページを参照してください。

要約 : この章では、セキュリティで保護されたデータ アクセス戦略の作成に役立つ推奨事項とガイドラインを示します。ここで扱うトピックは、ASP.NET からデータベースに対する Windows 認証、接続文字列の保護、資格情報をデータベースに保存する際のセキュリティ保護、SQL コード注入攻撃に対する防御、データベース ロールの使用などです。

目次

データ アクセス セキュリティの概要
認証
認定
セキュリティで保護された通信
最低限の特権による接続
最低限の特権を持つデータベース アカウントの作成
データベース接続文字列をセキュリティで保護して保存する
データベースとの照合によるユーザー認証
SQL コード注入攻撃
監査
SQL Server のプロセス ID
まとめ

Web ベースのアプリケーションを作成するときには、データのアクセスおよび保存に関してセキュリティで保護されたアプローチを使用することが必要です。この章では、データ アクセスに関する問題をいくつか取り上げ、説明します。この章の内容は、以下のことを行う際に役立ちます。

  • SQL Server® に接続するときの認証方法として、Microsoft® Windows® 認証と SQL 認証のどちらかを選択する。
  • 接続文字列をセキュリティで保護して保存する。
  • 最初の呼び出し元のセキュリティ コンテキストをデータベースまでフローするかどうかを決定する。
  • 接続プーリングを利用する。
  • SQL コード注入攻撃に対して防御する。
  • 資格情報をセキュリティで保護してデータベースに保存する。

この章では、ロールの使用に関連するさまざまなメリットとデメリットについても説明します。たとえば、データベース内のロールと中間層に適用するロール ロジックには、それぞれに長所と短所があります。最後に、データ アクセスに関する推奨事項を示します。

データ アクセス セキュリティの概要

データ アクセスに付随するセキュリティ上の主要な問題を図 12.1 に示します。

図 12.1 データ アクセスに付随するセキュリティ上の主要な問題

図 12.1 に示した問題については、この後の節で詳しく説明します。要点をまとめると、以下のようになります。

  1. データベース接続文字列をセキュリティで保護して保存する。この対策が特に重要となるのは、アプリケーションが SQL 認証を使用して SQL Server に接続する場合や、明示的なログオン資格情報を要求する Microsoft 以外のデータベースに接続する場合です。こうしたケースでは、接続文字列にクリア テキストのユーザー名とパスワードが含まれます。

  2. 適切な ID を使用してデータベースにアクセスする。データ アクセスを実行するには、呼び出しプロセスのプロセス ID、サービス ID、最初の呼び出し元の ID (偽装および委任モデル) のいずれかを使用します。どの ID を使用するかは、信頼されたサブシステム モデルと、偽装および委任モデルのどちらのデータ アクセス モデルを採用するかによって決まります。

  3. ネットワーク上でフローするデータをセキュリティで保護する。たとえば、SQL Server との間で送信されるログイン資格情報や機密データをセキュリティを保護します。

    メモ ログイン資格情報がネットワーク上で送信されるのは、Windows 認証ではなく SQL 認証を使用した場合だけです。

    SQL Server 2000 は SSL およびサーバー証明書をサポートします。IPSec を使用して、Web サーバーやアプリケーション サーバーなどのクライアント コンピュータとデータベース サーバーとの間で発生するトラフィックを暗号化することもできます。

  4. データベース側で呼び出し元を認証する。SQL Server は NTLM または Kerberos を使用する Windows 認証と SQL Server 組み込みの認証スキーム メカニズムを使用する SQL 認証をサポートします。

  5. データベース側で呼び出し元を認定する。個々のデータベース オブジェクトにアクセス許可を関連付けます。アクセス許可は、ユーザー、グループ、ロールに割り当てることができます。

SQL Server ゲートキーパー

SQL Server データ アクセスの主要なゲートキーパーを図 12.2 に示します。

図 12.2 SQL Server ゲートキーパー

主要なゲートキーパーは、以下のとおりです。

  • データベース接続文字列を保存するデータ ストア

  • 接続文字列に指定されたサーバー名によって決定される SQL Server ログイン

  • 接続文字列に指定されたデータベース名によって決定されるデータベース ログイン

  • 個々のデータベース オブジェクトに設定されたアクセス許可

    アクセス許可は、ユーザー、グループ、ロールに割り当てることができます。

信頼されたサブシステムと偽装および委任

データベース アクセスの細分度は、重要な検討事項の 1 つです。データベース側でユーザー レベルの認定が必要かどうか (偽装および委任モデルが必要)、それともアプリケーションの中間層でアプリケーション ロール ロジックを使用してユーザーを認定できるかどうか (信頼されたサブシステム モデル) について検討しなければなりません。

データベースにユーザー レベルの認定が必要な場合は、最初の呼び出し元を偽装する必要があります。この偽装および委任モデルはサポートされていますが、信頼されたサブシステム モデルを使用することをお勧めします。このモデルでは、最初の呼び出し元が IIS/ASP.NET ゲートでチェックされ、ロールにマップされてから、ロール メンバシップに基づいて認定されます。次に、アプリケーションのシステム リソースがアプリケーション レベルまたはロール レベルで認定されます。これには、サービス アカウントまたは ASPNET アカウントなどのアプリケーションのプロセス ID を使用します。

この 2 つのモデルを 図 12.3 に示します。

図 12.3 データベース アクセスの信頼されたサブシステム モデルと偽装および委任モデル

データ アクセスのために SQL Server に接続するときに考慮しなければならない重要な問題がいくつかあります。それらの問題については後の節で詳しく説明しますが、要点を以下にまとめておきます。

  • どのような認証方法を使用するのか。Windows 認証を使用するとセキュリティ レベルが向上しますが、ファイアウォールと信頼されていないドメインの問題によって SQL 認証を使用せざるを得ない場合があります。その場合は、アプリケーションにおいて SQL 認証のセキュリティ レベルをできるだけ高くする必要があります。これについては、後の「SQL 認証」で説明します。

  • 単一ユーザー ロールか、複数ユーザー ロールか。使用するアプリケーションは、データベース内のアクセス許可が固定されている単一アカウントを使用して SQL にアクセスすることが必要なのか、それともアプリケーションのユーザーによっては複数のロール ベース アカウントが必要になるのかについて検討します。

  • 呼び出し元の ID。データベースでは、認定または監査を実行するために、呼び出し元のコンテキストから最初の呼び出し元の ID を取得する必要があるかどうか、それとも信頼関係接続を使用して最初の呼び出し元の ID をアプリケーション レベルで渡すことができるかどうかについて検討します。

    オペレーティング システムが最初の呼び出し元の ID をフローするためには、中間層で偽装および委任が有効に設定されている必要があります。これによって、接続プーリングの有効性が著しく低下します。接続プーリングは可能ですが、サイズの小さいプールが個々のセキュリティ コンテキストごとに数多く生成され、接続が再利用されることはほとんどありません。

  • データベース サーバーとの間で機密データを送信するかどうか。Windows 認証では、ユーザーの資格情報はネットワーク上でデータベースまで送られませんが、従業員の個人情報や給与データなどのアプリケーションのデータに機密性がある場合は、IPSec または SSL を使用してセキュリティで保護する必要があります。

認証

ここでは、SQL Server に対してクライアントを認証する方法と、SQL Server に接続する前にクライアント アプリケーション内でデータベース アクセスに使用する ID を選択する方法について説明します。

Windows 認証

Windows 認証は、以下の理由で SQL 認証より高いセキュリティ レベルを実現します。

  • 資格情報は管理され、ネットワーク上で転送されません。
  • 接続文字列にユーザー名とパスワードを含める必要がありません。
  • パスワードの有効期限、最低文字数、複数の無効ログオン要求後のアカウント ロックアウトなどの機能によって、ログオン セキュリティが強化されます。これによってディクショナリ攻撃の脅威が軽減します。

Windows 認証は次のような場合に使用します。

  • 信頼されたサブシステム モデルで使用しており、単一固定 ID を使用して SQL Server に接続します。ASP.NET から接続する場合は、Web アプリケーションで偽装が無効に設定されていることが前提となります。

    このシナリオでは、ASP.NET プロセス ID またはサービス コンポーネント ID (Enterprise Services サーバー アプリケーションの実行に使用されるアカウントから取得) を使用します。

  • 委任を使用して、最初の呼び出し元のセキュリティ コンテキストを意図的に委任します。このとき、データベース接続プーリングが無効になるため、アプリケーション スケーラビリティが低下します。

Windows 認証を使用して SQL Server に接続するときには、以下の点に留意してください。

  • ASP.NET プロセス アカウントには最低限の特権の原則を適用します。ASP.NET プロセス アカウントには、LogonUser API 呼び出しを実行可能にする "オペレーティング システムの一部として動作する" 特権を与えないでください。
  • 追加特権を必要とするコードを特定し、それをアウト プロセス Enterprise Services アプリケーション内で動作するサービス コンポーネントに挿入します。

詳細情報

ASP.NET からネットワーク リソースへのアクセスと ASP.NET を実行するアカウントの選択および設定の詳細については、「第 8 章 ASP.NET セキュリティ」を参照してください。

Windows 認証の使用

Windows 認証を使用して ASP.NET アプリケーション、または Web サービスや ASP.NET がホストするリモート コンポーネントから SQL Server に接続するときには、以下の方法が可能です。

  • ASP.NET プロセス ID を使用する。
  • ASP.NET 内の固定 ID を使用する。
  • サービス コンポーネントを使用する。
  • LogonUser API を使用し、特定の ID を偽装する。
  • 最初の呼び出し元の ID を使用する。
  • 匿名インターネット ユーザー アカウントを使用する。

推奨事項

ローカル ASP.NET プロセス ID のパスワードを Web サーバー上で既知の値に変更し、ユーザー名とパスワードが同一のローカル ユーザーを作成してデータベース サーバー上にミラー化されたアカウントを作成することをお勧めします。この設定とその他のアプローチについては、以下で詳しく説明します。

ASP.NET プロセス ID の使用

ASP.NET アプリケーションまたは Web サービスや ASP.NET がホストするリモート コンポーネントから直接 SQL Server に接続する場合は、ASP.NET プロセス ID を使用します。これが通常のアプローチであり、アプリケーションは信頼関係境界を定義します。つまり、データベースは ASP.NET アカウントを信頼してデータベース オブジェクトにアクセスさせます。

この場合、次に挙げるアカウントのいずれかを使用できます。

  • ミラー化された ASPNET ローカル アカウント
  • ミラー化されたカスタム ローカル アカウント
  • カスタム ドメイン アカウント

ミラー化された ASPNET ローカル アカウントを使用する

これが最も単純なアプローチであり、ターゲット データベースがあり、しかもローカル データベース サーバー アカウントの管理を制御できるケースで一般的に使用されるものです。この方法では、最低限の特権を持つローカル ASPNET アカウントを使用して ASP.NET を実行し、データベース サーバーに複製されたアカウントを作成します。

メモ このアプローチには、もう 1 つ利点があります。信頼できないドメイン間やファイアウォールで隔てられたコンピュータ間にも適用できることです。ファイアウォールでは、Windows 認証のサポートに必要な数のポートが開いていない場合があります。

ミラー化されたカスタム ローカル アカウントを使用する

このアプローチは、既定の ASPNET アカウントを使用しない点を除き、前のアプローチと同じです。このアプローチを使用する場合は、次の 2 点に留意してください。

  • 適切なアクセス許可と特権を持つカスタム ローカル アカウントを作成する必要があります。

    詳細については、このガイドの「パート IV : 参照」の「ASP.NET の実行に使用するカスタム アカウントの作成方法」を参照してください。

  • .NET Framework のインストール プロセス中に作成される既定のアカウントは使用しません。企業によっては、既定のインストール アカウントを使用してはならないと定められていることがあります。これによって、アプリケーションのセキュリティ レベルが向上する可能性があります。

    詳細については、Sans Top 20「G2 - Accounts with No Passwords or Weak Passwords」(http://www.sans.org/top20/ Non-MS link) (英語情報) を参照してください。

カスタム ドメイン アカウントを使用する

このアプローチは、ローカル アカウントではなく最低限の特権を持つドメイン アカウントを使用する点を除き、前のアプローチと似ています。このアプローチは、クライアント コンピュータとサーバー コンピュータが同じドメインまたは信頼関係にあるドメインに存在することを前提とします。最大の利点は、資格情報がコンピュータ間で共有されないことです。コンピュータは単にドメイン アカウントにアクセス権を与えるだけです。さらに、ドメイン アカウントを使用することにより、管理も容易になります。

ミラー化された ASPNET プロセス ID の実装

ミラー化されたアカウントを使用して ASP.NET からデータベースに接続するためには、以下の設定が必要です。

  • Web サーバーのユーザー マネージャを使用して、ASPNET アカウントのパスワードを既知の方法による強力なパスワード値に変更します。

    重要 ASPNET アカウントのパスワードを変更すると、ローカル コンピュータ上のローカル セキュリティ機関 (LSA) にあるパスワードは Windows セキュリティ アカウント マネージャ (SAM) データベース内のアカウント パスワードと一致しなくなります。既定値の AutoGenerate に戻す場合は、以下を行ってください。
    Aspnet_regiis.exe を実行して、ASP.NET を既定の設定に戻します。詳細については、Microsoft サポート技術情報の Knowledge Base の文書 306005「[HOWTO] IIS を削除して再インストールした後、IIS マッピングを修復する方法」を参照してください。これによって、新しいアカウントと新しい Windows セキュリティ ID (SID) が作成されます。このアカウントのアクセス許可は既定値に設定されます。したがって、元の ASPNET アカウントに設定していたアクセス許可および特権を明示的に再適用する必要があります。

  • Machine.config でパスワードを明示的に設定します。

<processModel userName="machine" password="YourStrongPassword"
Windows ACL を使用して、machine.config を不正アクセスから保護する必要があります。たとえば、IIS 匿名インターネット ユーザー アカウントによる Machine.config へのアクセスを制限します。
  • ユーザー名とパスワードが同じミラー化されたアカウントをデータベース サーバーに作成します。

  • SQL データベース内に、ローカル ASPNET アカウントのサーバー ログインを作成し、そのログインを必要なデータベース内のユーザー アカウントにマップします。次に、データベース ユーザー ロールを作成し、そのロールにデータベース ユーザーを追加して、そのロールに適したデータベース アクセス許可を設定します。

    詳細については、後の「最低限の特権を持つデータベース アカウントの作成」を参照してください。

Windows 認証を使用して SQL Server に接続する

Windows 認証を使用して SQL Server に接続するには

  • クライアント アプリケーションで、"Trusted Connection=Yes" または "Integrated Security=SSPI" を含む接続文字列を使用します。SQL Server の認証方法が Windows 認証に設定されている場合、これら 2 つの文字列は同じ意味であり、どちらも Windows 認証を有効にします。次に、例を示します。
server=MySQL; Integrated Security=SSPI; database=Northwind
**メモ** 要求を発行するクライアント、つまり SQL Server によって認証されるクライアントの ID は、スレッドが偽装している場合クライアントのスレッド偽装トークン、またはクライアントの現在のプロセス トークンによって決まります。

ASP.NET 内の固定 ID を使用する

このアプローチでは、次に示す Web.config の要素を使用して、指定された固定 ID を偽装するように ASP.NET アプリケーションを設定します。

<identity impersonate="true" 
          userName="YourAccount" 
          password="YourStrongPassword" />

この ID が、データベースなどのネットワーク リソースに接続するときに使用する既定の ID となります。

このアプローチは、次に挙げる 2 つの理由から、.NET Framework Version 1.0 で使用することはお勧めできません。

  • ユーザー名とパスワードがクリア テキストで Web 空間、つまり仮想ディレクトリの Web.config に保存されます。

  • Windows 2000 上の ASP.NET には "オペレーティング システムの一部として動作する" 特権が必要です。この要件は、Microsoft Windows Server 2003 には当てはまりません。

    この強力な特権の詳細については、1999 年 8 月発行の「Microsoft Systems Journal」の「Security Briefs」(英語情報) を参照してください。

.NET Framework Version 1.1 には、このシナリオを Windows 2000 環境で構築した場合に有効な以下の拡張機能が追加されています。

  • 資格情報が暗号化されます。
  • ログオンが IIS プロセスによって実行されるため、ASP.NET には "オペレーティング システムの一部として動作する" 特権が必要ありません。

サービス コンポーネントの使用

データ アクセス コードだけを収めたサービス コンポーネントを作成できます。サービス コンポーネントでは、特定の ID で実行される Enterprise Services (COM+) サーバー アプリケーションでコンポーネントをホストするか、LogonUser API を使用して偽装を実行するコードを作成することによって、データベースにアクセスできます。

アウト プロセスのサービス コンポーネントを使用すると、特にプロセスごとに実行 ID が異なる場合など、プロセス ホップによって攻撃が困難になるため、セキュリティ レベルが向上します。また、より多くの特権を必要とするコードを、他のアプリケーション コードから隔離できるという利点もあります。

LogonUser を呼び出し、特定の Windows ID を偽装する

LogonUser を直接 ASP.NET から呼び出してはなりません。Windows 2000 の場合、このアプローチでは ASP.NET プロセス ID に "オペレーティング システムの一部として動作する" 権限を与える必要があります。

前に説明したように、Enterprise Services サーバー アプリケーション内のサービス コンポーネントを使用し、ASP.NET プロセスの外部で LogonUser を呼び出す方が望ましいアプローチです。

最初の呼び出し元の ID を使用する

このアプローチを使用するためには、Kerberos 委任を使用し、ASP.NET またはサービス コンポーネントにおいて、データベースに対して呼び出し元を偽装する必要があります。

ASP.NET で偽装するには、アプリケーションの Web.config に次の行を追加します。

<identity impersonate="true" />

サービス コンポーネントで偽装するには、CoImpersonateClient を呼び出します。

匿名インターネット ユーザー アカウントを使用する

これは前に挙げたアプローチのバリエーションです。アプリケーションがフォーム認証または Passport 認証を使用すると、IIS 匿名認証の使用を意味しますが、この場合、アプリケーションの web.config で偽装を有効にし、データベース アクセスに匿名インターネット ユーザー アカウントを使用できます。

<identity impersonate="true" />

IIS で匿名認証を有効にした場合、Web アプリケーションのコードは匿名インターネット ユーザーの偽装トークンで実行されることになります。Web をホストする環境では、このアプローチには複数の Web アプリケーションからのデータベース アクセスを個別に監査、追跡できるという利点があります。

詳細情報

Windows 認証を使用できない場合

アプリケーション シナリオによっては、Windows 認証を使用できない場合があります。次に、例を示します。

  • データベース クライアントとデータベース サーバーが Windows 認証を使用できないファイアウォールで分離されている場合。

  • アプリケーションが複数の ID を使用して 1 つまたは複数のデータベースに接続する必要がある場合。

  • SQL Server 以外のデータベースに接続する場合。

  • コードを特定の Windows ユーザーとして実行する、セキュリティで保護されたメカニズムを ASP.NET に組み込んでいない場合。最初の呼び出し元のセキュリティ コンテキストを送信できない、または送信したくない場合や、エンド ユーザーにログオンを許可するのではなく専用のサービス アカウントを使用することが必要な場合があります。

    Machine.config (<processModel> 要素) または Web.config (<identity> 要素) にユーザー名とパスワードをして、ASP.NET ワーカー プロセスまたはアプリケーションを実行すると、標準の SQL 資格情報を保護するメカニズムを組み込んだ場合より、セキュリティ レベルは低くなります。

こうしたケースでは、SQL 認証、またはデータベース固有の認証メカニズムを使用し、以下の対策を講じる必要があります。

  • アプリケーション サーバー上のデータベース ユーザー資格情報を保護する。
  • データベース ユーザーの資格情報をサーバーからデータベースへの送信中に保護する。

SQL 認証を使用する場合には、さまざまな方法で SQL 認証のセキュリティ レベルを向上させることができます。それらの方法については、次の「SQL 認証」で説明します。

SQL 認証

アプリケーションで SQL 認証を使用する必要がある場合は、以下の点に留意してください。

  • 最低限の特権を持つアカウントを使用して SQL に接続します。
  • ネットワーク上で送信される資格情報をセキュリティで保護する必要があります。
  • 資格情報が含まれる SQL 接続文字列をセキュリティで保護する必要があります。

接続文字列の種類

ユーザー名とパスワードから成る資格情報を使用して SQL Server データベースに接続する場合、接続文字列は次のようになります。

SqlConnectionString = "Server=YourServer;
                      Database=YourDatabase;
                      uid=YourUserName;pwd=YourStrongPassword;"

同じコンピュータにインストールされた SQL Server の特定のインスタンスに接続する (この機能は SQL Server 2000 以上でのみ使用可能) 必要がある場合、接続文字列は次のようになります。

SqlConnectionString = "Server=YourServer\Instance;
                      Database=YourDatabase;uid=YourUserName;
                      pwd=YourStrongPassword;"

ネットワーク資格情報を使用して SQL Server に接続する場合は、次のように Integrated Security 属性、または Trusted Connection 属性を使用し、ユーザー名とパスワードを省略します。

SqlConnectionString = "Server=YourServer;
                       Database=YourDatabase;
                       Integrated Security=SSPI;"

または

SqlConnectionString = "Server=YourServer;
                      Database=YourDatabase;
                      Trusted_Connection=Yes;"

明示的な資格情報であるユーザー名とパスワードを使用して Oracle データベースに接続する場合、接続文字列は次のようになります。

SqlConnectionString = "Provider=MSDAORA;Data Source=YourDatabaseAlias;
                      User ID=YourUserName;Password=YourPassword;"

詳細情報

UDL (Universal Data Link) ファイルを使用した接続の詳細については、Microsoft サポート技術情報の Knowledge Base の文書 308426「[HOW TO] Visual C# .NET で OleDbConnection オブジェクトを持つデータ リンク ファイルを使用する方法」を参照してください。

接続に使用する SQL アカウントの選択

データ アクセスには、組み込みアカウントの "sa" や "db_owner" は使用しないでください。これらのアカウントではなく、最低限の特権と強力なパスワードを持つアカウントを使用します。

次のような接続文字列は使用しないでください。

SqlConnectionString = "Server=YourServer\Instance;
                       Database=YourDatabase; uid=sa; pwd=;"

次の例のような最低限の特権と強力なパスワードを持つアカウントを使用します。

SqlConnectionString= "Server=YourServer\Instance;
                      Database=YourDatabase;
                      uid=YourStrongAccount; 
                      pwd=YourStrongPassword;"

このようなアカウントを使用しても、資格情報をプレーン テキストのまま Web.config ファイルに保存してよいわけではありません。 これまで行ってきた設定は、最低限の特権を持つアカウントを使用することによって、不正アクセスが発生した場合の損害を軽減するためのものです。セキュリティ レベルをさらに高めるには、資格情報を暗号化することが必要です。

メモ SQL Server をインストールするときに、大文字と小文字を区別する並べ替え順序を選択した場合は、ログイン ID も大文字と小文字が区別されます。

資格情報をネットワーク上で転送する

SQL 認証によって SQL Server に接続するときには、ユーザー名とパスワードがクリア テキストのままネットワーク上で送信されます。これはセキュリティ上の重大な問題です。アプリケーション サーバーまたは Web サーバーとデータベース サーバーとのチャネルをセキュリティで保護する方法の詳細については、後の「セキュリティで保護された通信」を参照してください。

SQL 接続文字列をセキュリティで保護する

ユーザー名とパスワードをクリア テキストのまま構成ファイルに保存してはなりません。接続文字列を保存するときにセキュリティで保護する方法の詳細については、後の「データベース接続文字列をセキュリティで保護して保存する」」を参照してください。

SQL Server 以外のデータベースに対する認証

SQL Server 以外のデータベースに接続するときに浮上する代表的な問題は、SQL 認証を使用する必要がある場合に生じる問題と似ています。ターゲット リソースが Windows 認証をサポートしていない場合は、明示的な資格情報を渡す必要があります。このようなシナリオをセキュリティで保護するためには、接続文字列を保存するときにセキュリティ対策を施すことが必要であり、さらにネットワーク通信をセキュリティで保護することも資格情報の傍受を防ぐために必要になります。

詳細情報

認定

SQL Server では、いくつかのロール ベースの認定アプローチが可能です。それらのアプローチは、SQL Server がサポートする以下の 3 種類のロールを基礎とします。

  • ユーザー定義のデータベース ロール このロールによって、データベース内に同じセキュリティ特権を持つユーザーをグループ化できます。ユーザー データベース ロールに Windows ユーザー アカウントまたはグループ アカウントを追加し、それらのロールを使用してストアド プロシージャ、テーブル、ビューなどの個々のデータベース オブジェクトに対するアクセス許可を設定します。

  • アプリケーション ロール このロールは、オブジェクト権限を設定するときに使用する点で、ユーザー データベース ロールに似ています。ただし、ユーザー データベース ロールとは異なり、ユーザーやグループは追加しません。このロールは、組み込みストアド プロシージャを使用してアプリケーションによってアクティブ化する必要があります。ロールがアクティブであれば、そのロールに与えられたアクセス許可によって、アプリケーションのデータ アクセス能力が決まります。

    アプリケーション ロールでは、データベース管理者がアプリケーションに対して指定されたデータベース オブジェクトへのアクセス権を付与できます。この点は、ユーザーにアクセス許可を付与するアプローチと対照的です。

  • 固定データベース ロール SQL Server は、db_datareader や db_datawriter などの固定サーバー ロールも提供します。これらの組み込みロールはすべてのデータベースに存在し、ユーザーにデータベース内での読み取りに関連するおよびその他の一般的に使用されるアクセス許可を速やかに与えるのに役立ちます。

各種ロールおよび固定サーバー ロール (固定データベース ロールに似ているが、データベース レベルではなくサーバー レベルで適用されるロール) の詳細については、「SQL Server Books Online」を参照してください。

複数のデータベース ロールを使用する

アプリケーションに複数のユーザー カテゴリがあり、各カテゴリのユーザーがデータベース内において同じアクセス許可を必要とする場合は、アプリケーションに複数のロールが必要となります。

各ロールには、それぞれに異なるアクセス許可のセットが必要です。たとえば、インターネット ユーザー ロールであれば、大部分のデータベース テーブルに対する読み取り専用アクセス許可が必要ですが、管理者ロールまたはオペレータ ロールには読み取りおよび書き込みのアクセス許可が必要です。

オプション

これらのシナリオでは、SQL Server でロール ベースの認定を行う方法として 2 つのオプションがあります。

  • ユーザー定義の SQL Server データベース ロール このロールでは、データベース内で同じセキュリティ アクセス許可を持つユーザーのグループごとにデータベース オブジェクトにアクセス許可を割り当てます。
    ユーザー定義のデータベース ロールを使用するときには、ゲートでチェックし、ユーザーをロールにマップして (ASP.NET Web アプリケーション内や Enterprise Services サービス アプリケーションの中間層に位置するサービス コンポーネント内)、それぞれユーザー定義のデータベース ロールにマップされた複数の ID を使用してデータベースに接続します。
  • SQL アプリケーション ロール このロールは、データベース オブジェクトにアクセス許可を割り当てるときに使用する点で、ユーザー定義のデータベース ロールに似ています。ただし、ユーザー定義のデータベース ロールとは異なり、メンバは属しておらず、個々のアプリケーションが組み込みストアド プロシージャを使用してアクティブ化します。
    アプリケーション ロールを使用するときには、ゲートでチェックし、ユーザーをロールにマップし、単一の信頼されたサービス ID を使用してデータベースに接続して、適切な SQL アプリケーション ロールをアクティブ化します。

ユーザー定義のデータベース ロール

ユーザー定義のデータベース ロールを使用する場合は、以下のことが必要です。

  • データベース アクセスに使用する複数のサービス アカウントを作成します。
  • 各アカウントをユーザー定義のデータベース ロールにマップします。
  • データベース内の各ロールごとに必要なデータベース アクセス許可を設定します。
  • ASP.NET Web アプリケーション、Web サービス、または中間層コンポーネントなどのアプリケーション内でユーザーを認定し、データ アクセス層のアプリケーション ロジックを使用して、データベースへの接続に使用するアカウントを決定します。この決定は、呼び出し元のロール メンバシップに基づいて行います。
    宣言文を使用して、個々のメソッドを特定のロールに属すユーザーだけに許可するように設定できます。さらに、メソッド コードに強制的なロール チェックを挿入し、厳密なロール メンバシップを決定します。これによって、使用する接続が決まります。

このアプローチを図 12.4 に示します。

図 12.4 複数の SQL ユーザー データベース ロールを使用した SQL Server への接続

このシナリオで推奨される Windows 認証を使用するためには、いずれか 1 つの Windows ID を偽装するためLogonUser API を使用してコードを作成し、サービス対象のアウト プロセス コンポーネントに挿入します。

SQL 認証の場合は、アプリケーションのロール ベース ロジックごとに異なるユーザー名とパスワードを含む接続文字列を使用します。

詳細情報

データベース接続文字列を安全に保存する方法の詳細については、後の「データベース接続文字列をセキュリティで保護して保存する」を参照してください。

アプリケーション ロール

SQL アプリケーション ロールを使用する場合は、以下のことが必要となります。

  • データベース アクセスに使用する 1 つのサービス アカウントを作成します (ASP.NET ワーカー プロセスまたは Enterprise Services アプリケーションを実行するプロセス アカウント)。
  • データベース内に SQL アプリケーション ロールを作成します。
  • データベース内の各ロールごとに必要なデータベース アクセス許可を設定します。
  • ASP.NET Web アプリケーション、Web サービス、または中間層コンポーネントなどのアプリケーション内でユーザーを認定し、データ アクセス層のアプリケーション ロジックを使用して、データベース内でアクティブ化するアプリケーション ロールを決定します。この決定は、呼び出し元のロール メンバシップに基づいて行います。

このアプローチを図 12.5 に示します。

図 12.5 複数の SQL アプリケーション ロールの使用

図 12.5 では、データベースへのアクセスに使用される "ServiceIdentity1" という ID は、通常、ASP.NET ワーカー プロセスまたは Enterprise Services サーバー アプリケーション プロセス ID から取得されます。

このアプローチでは、同じサービス ID つまり同じ接続を使用して SQL Server に接続します。SQL アプリケーション ロールは、呼び出し元のロール メンバシップに基づき、組み込みストアド プロシージャ sp_setapprole によってアクティブ化します。このストアド プロシージャには、ロール名とパスワードが必要です。

このアプローチを使用する場合は、資格情報のロール名とパスワードをセキュリティで保護したうえで保存する必要があります。詳しい説明と機密情報のストレージ テクニックについては、後の「データベース接続文字列をセキュリティで保護して保存する」」を参照してください。

SQL アプリケーション ロールの制約

SQL アプリケーション ロールを使用する場合には、以下の点に留意してください。

  • SQL アプリケーション ロールの資格情報を管理する必要があります。接続ごとにロール名とパスワードを送信する sp_setapprole ストアド プロシージャを呼び出します。SQL アプリケーション ロールをマネージ コードからアクティブ化する場合は、アセンブリに埋め込まれているクリア テキストのパスワードが安全ではありません。

  • SQL アプリケーション ロールの資格情報は、クリア テキストでデータベースに渡されます。アプリケーション サーバーとデータベース サーバーの間に IPSec または SSL を配置して、ネットワーク上の資格情報をセキュリティで保護する必要があります。

  • ある接続で SQL アプリケーション ロールをアクティブ化した後で、非アクティブにすることはできません。SQL アプリケーション ロールは、接続が閉じるまでアクティブのままです。また、同一の接続で複数のロールを切り替えることもできません。

  • SQL アプリケーション ロールは、アプリケーションが単一の固定 ID を使用してデータベースに接続する場合のみ使用してください。つまり、SQL アプリケーション ロールを使用するのは、アプリケーションが信頼されたサブシステム モデルを使用する場合に限られます。
    最初の呼び出し元のコンテキストを使用してデータベースに接続した場合など接続のセキュリティ コンテキストが変わると、SQL アプリケーション ロールに対して接続プーリングが有効になりません。

    詳細については、Microsoft サポート技術情報の Knowledge Base の文書 229564「PRB: SQL Application Role Errors with OLE DB Resource Pooling」(英語情報) を参照してください。

セキュリティで保護された通信

ほとんどのアプリケーション シナリオでは、アプリケーション サーバーとデータベース間の通信リンクをセキュリティで保護する必要があります。以下の 2 点を保証することが必要です。

  • メッセージの機密性 データを暗号化することによって、そのプライバシーを保護する必要があります。
  • メッセージの整合性 データに署名を付けることによって、改変されていないことを保証する必要があります。

シナリオによって、アプリケーション サーバーとデータベース サーバーの間で送信されるすべてのデータをセキュリティで保護しなければならない場合もあれば、特定の接続で送信される特定のデータ項目だけを保護すればよい場合もあります。次に、例を示します。

  • イントラネットの人事アプリケーションでは、クライアントとデータベース サーバーの間で送信される従業員データの中に機密性を持つものがあります。
  • バンキング アプリケーションなどのインターネット シナリオでは、アプリケーション サーバーと データベース サーバーの間で送信されるすべてのデータをセキュリティで保護する必要があります。
  • SQL 認証を使用する場合は、ユーザー名とパスワードがネットワーク監視ソフトウェアによって盗まれないように、通信リンクをセキュリティで保護する必要があります。

オプション

アプリケーション サーバーとデータベース サーバーの通信リンクをセキュリティで保護するには、2 つの方法があります。

  • IPSec

  • SSL (SQL Server コンピュータでサーバー証明書を使用する)

    メモ SSL をサポートする SQL Server 2000 を実行する必要があります。これ以前のバージョンは SSL をサポートしません。クライアントには、SQL Server 2000 クライアント ライブラリがインストールされている必要があります。

アプローチの選択

IPSec と SSL のどちらを使用するかは、ファイアウォールの特徴やオペレーティング システムとデータベースのバージョンなど、主として環境に関連する条件に基づいて決定します。

メモ IPSec は、本来、アプリケーション レベルのセキュリティを担うものではありません。現在、縦深防御メカニズムとして使用したり、安全性の低いアプリケーションのセキュリティをコードの変更なしに強化するために用いたりするほか、SSL など TLS 以外のプロトコルをネットワーク回線攻撃から保護する目的にも使用します。

詳細情報

最低限の特権による接続

最低限の特権でデータベースに接続することは、その接続ではデータベースの操作に最低限必要な権限しか使用できないことを意味します。簡単に言えば、sa またはデータベース所有者のアカウントでデータベースに接続した場合とは異なります。理論的には、現在のユーザーがレコードの追加および更新を許可されていないと、対応するアカウント (特定のロールを表す ID に集約される) ではデータベース レコードを追加、更新することはできません。

SQL Server に接続するときには、接続アプローチがデータベース認定に必要な細分度に対応している必要があります。データベースが何を信頼するかについて検討することが必要です。データベースが信頼する対象には、次のものがあります。

  • アプリケーション
  • アプリケーションで定義されたロール
  • 最初の呼び出し元

データベースがアプリケーションを信頼する場合

データベースの使用について認定を行う財務アプリケーションについて考えてみます。この財務アプリケーションは、ユーザーを認証し、アクセスを許可します。この場合、SQL ログインまたは SQL ログインにマップされた Windows アカウントに対応する 1 つの信頼されたアカウントによって接続を管理できます。Windows 認証を使用する場合、ASP.NET ワーカー プロセス、Enterprise Services サービス アプリケーションなどの呼び出し側アプリケーションのプロセス ID でデータベースにアクセスできることを意味します。

認定の観点から見ると、このアプローチはきわめて粗雑なものです。接続を確立する ID は、アプリケーションが必要とするデータベース オブジェクトおよびリソースのすべてにアクセスできるからです。このアプローチの利点は、接続プーリングを使用でき、1 つのアカウントを認定するだけで済むため、管理が容易であることです。ただし、すべてのユーザーが同じ接続特権を持つという欠点があります。

データベースがさまざまなロールを信頼する場合

アプリケーションに定義されたロールごとに、データベースに対する個別の信頼関係接続プールを使用できます。たとえば、金銭出納係用の接続と管理職用の接続を別々にすることが可能です。

これらの接続では、Windows 認証を使用してもしなくてもかまいません。Windows 認証には、資格情報が管理され、ネットワーク上で資格情報が送信されないという利点があります。ただし、Windows 認証はプロセス レベルとアプリケーション レベルのどちらでも 1 つのデータベース接続を使用する場合と同じように使用できますが、複数の ID つまり各ロールにつき 1 つの ID が必要であることに伴う問題も生じます。

多くのアプリケーションは LogonUser API を使用して Windows アクセス トークンを確立します。このアプローチには、次に挙げる 2 つの問題があります。

  • 資格情報の管理が必要となります。アプリケーション側でアカウントのユーザー名とパスワードをセキュリティで保護したうえで保存する必要があります。

  • LogonUser API では、呼び出し側プロセスのアカウントが "オペレーティング システムの一部として動作する" 特権を持っている必要があります。つまり、この特権を ASP.NET プロセス アカウントに与える必要がありますが、これはお勧めできません。代替策として SQL 認証を使用するという方法もありますが、その場合は、サーバーおよびネットワーク上で資格情報を保護する必要があります。

    メモ この LogonUser に伴う制約は Windows Server 2003 で解決されています。

データベースが最初の呼び出し元を信頼する場合

このケースでは、最初の呼び出し元を複数の層を経由してデータベースまでフローさせる必要があります。これは、クライアントが、あるコンピュータから次のコンピュータへホップするために、ネットワーク資格情報が必要となることを意味します。そのためには Kerberos 委任が必要です。

このソリューションでは、最初の呼び出し元の ID がわかっているため、データベース オブジェクトに対するユーザー単位のアクセス許可を設定できることから、データベース内で精密な認定が可能ですが、それがアプリケーションのパフォーマンスとスケーラビリティの低下をもたらします。接続プーリングは (有効なままであっても) 無効になります。

最低限の特権を持つデータベース アカウントの作成

ここでは、最低限の特権を持つデータベース アカウントの作成手順について説明します。この手順は、ほとんどのデータベース管理者は熟知していますが、開発者にはあまり知られていません。そのため、多くの開発者が "sa" やデータベース所有者のアカウントを使用してアプリケーションを実行しようとします。

これらのアカウントを使用すると、開発環境からテスト環境、さらに実稼働環境へと移行するときに問題が発生します。制限のない環境から厳密に制御された環境へと移行することによって、アプリケーションが正しく機能しなくなるからです。

まず、SQL アカウントまたはユーザーまたはグループの Windows アカウント (ユーザーまたはグループ) の SQL ログインを作成します。そして、そのログインをデータベース ユーザー ロールに追加し、そのロールにアクセス許可を割り当てます。

■ SQL のデータ アクセス アカウントを作成するには

  1. 新しいユーザー アカウントを作成し、Windows グループに追加します。複数のユーザーがいる場合は、グループを使用します。複製された ASP.NET プロセス アカウントなど、扱うアプリケーション アカウントが 1 つだけである場合は、そのアカウントを Windows グループに追加しなくてもかまいません。

  2. 作成したユーザーまたはグループの SQL Server ログインを作成します。

    • Enterprise Manager を起動し、使用するデータベース サーバーのセキュリティ フォルダを展開します。
    • [ログイン] を右クリックして [新規ログイン] を選択します。
    • [名前] ボックスに Windows グループ名を入力し、[OK] をクリックして [SQL Server ログイン プロパティ] ダイアログ ボックスを閉じます。
  3. データベースに新しいデータベース ユーザーを作成します。このユーザーは SQL Server ログインにマップされます。

    • Enterprise Manager で、データベース フォルダを展開し、作成したログインでアクセスするデータベースを展開します。
    • [ユーザー] を右クリックし、[新規データベース ユーザー] をクリックします。
    • 前のステップで作成したログイン名を選択します。
    • ユーザー名を指定します。
    • 以下の説明に従ってアクセス許可を設定します。
  4. データベース ユーザーに対し、アクセスする必要のあるテーブルの "選択" アクセス許可と必要なストアド プロシージャの "実行" アクセス許可を与えます。

    メモ ストアド プロシージャとテーブルの所有者が同一人物であり、そのテーブルには常にストアド プロシージャを使用してアクセスするしたり、テーブルに直接アクセスする必要がない場合は、ストアド プロシージャの実行アクセス許可を付与するだけで十分です。これは、所有権の連鎖の考え方に基づくものです。詳細については、「SQL Server Books Online」を参照してください。

  5. ユーザー アカウントにデータベース内のすべてのビューおよびテーブルに対するアクセス許可を与える場合は、そのアカウントを db_datareader ロールに追加します。

データベース接続文字列をセキュリティで保護して保存する

データベース接続文字列を保存する場所とアプローチには、いくつかの選択肢があります。それぞれセキュリティ レベルが異なり、設定の柔軟性にも違いがあります。

オプション

接続文字列を保存する主要な方法を以下に示します。

  • DPAPI で暗号化する。
  • Web.config または Machine.config にクリア テキストで保存する。
  • UDL ファイル。
  • カスタム テキスト ファイル。
  • レジストリ。
  • COM+ カタログ。

DPAPI の使用

Windows 2000 以上では、DPAPI (Win32® Data Protection API) を使用したデータの暗号化および解読が可能です。DPAPI は暗号化 (Crypto API) の一部であり、Crypt32.dll に実装されています。DPAPI は CryptProtectData と CryptUnprotectData という 2 つのメソッドから成ります。

DPAPI の利点は、アプリケーションで暗号化を使用する場合に生じるキー管理の問題を解決できることです。暗号化によってデータは保護されますが、キーのセキュリティを確保するための追加処置が必要になります。DPAPI では、DPAPI 関数を呼び出すコードに関連付けられたユーザー アカウントのパスワードを利用して、暗号化キーを生成します。したがって、オペレーティング システムがキーを管理するため、アプリケーションではキーの管理が不要となります。

LSA の欠点

多くのアプリケーションが、機密情報を保存するためにローカル セキュリティ機関 (LSA) を使用しています。DPAPI は、次のような点で LSA より優れています。

  • LSA を使用するためには、プロセスに管理特権が必要となります。その場合、ハッカーが管理権限を持つプロセスを悪用して被害を及ぼす可能性がきわめて高くなるため、セキュリティ上の問題となります。
  • LSA では、機密情報のストレージ用に限られた数のスロットしか使用できず、しかもその多くはシステムが使用しています。

コンピュータ ストアとユーザー ストア

DPAPI は、コンピュータ ストアとユーザー ストアのどちらでも使用できます。ユーザー ストア はユーザー プロファイルがロードされていることが必要です。DPAPI の既定のデータ ストアは、ユーザー ストアです。コンピュータ ストアに変更するには、DPAPI 関数に CRYPTPROTECT_LOCAL_MACHINE フラグを渡します。

ユーザー プロファイルを使用することによって、機密情報にアクセスできるユーザーを制限することになるため、セキュリティが強化されます。データを暗号化したユーザーだけが暗号化を解除できます。ただし、ASP.NET Web アプリケーションで DPAPI を使用する場合、ユーザー プロファイルを使用するためには、そのロードおよびアンロードを行うコードを作成する必要があります。ASP.NET はユーザー プロファイルを自動的にロードしません。

コンピュータ ストアを使用すると、ユーザー プロファイルに関連する操作が不要なので、追加コードを作成しなくて済みます。ただし、エントロピ パラメータを使用しないと、すべてのユーザーがデータの暗号化を解除できるため、セキュリティが弱くなりますエントロピとは、機密情報の解読をより困難にするランダム値です。エントロピ パラメータの使用に伴う問題は、このパラメータを保存するときにアプリケーションにおいてセキュリティで保護する必要があるため、キー管理の操作が増えることです。

メモ コンピュータ ストアで DPAPI を使用する場合は、暗号化された文字列が特定のコンピュータに固有のものであるため、コンピュータごとに暗号化されたデータを生成する必要があります。暗号化されたデータをファームまたはクラスタ内のコンピュータ間でコピーしないでください。

ユーザー ストアで DPAPI を使用する場合は、ローミング ユーザー プロファイルによって、どのコンピュータでもデータの暗号化を解除できます。

DPAPI 実装ソリューション

ここでは、2 つの DPAPI 実装ソリューションを挙げ、ASP.NET Web アプリケーションから DPAPI を使用して接続文字列をセキュリティで保護する方法について説明します。ここで説明する実装ソリューションは次のとおりです。

  • Enterprise Services から DPAPI を使用する。 このソリューションでは、ユーザー ストアで DPAPI を使用できます。
  • ASP.NET から直接 DPAPI を使用する。 このソリューションでは、コンピュータ ストアで DPAPI を使用できます。DPAPI を ASP.NET Web アプリケーションから直接呼び出すため、開発作業が容易です。

Enterprise Services から DPAPI を使用する

このソリューションでは、ASP.NET Web アプリケーションは DPAPI を呼び出してユーザー ストアを使用することはできません。そのためにはユーザー プロファイルがロードされている必要があるからです。Web アプリケーションを実行するために使用される ASPNET アカウントは非インタラクティブ アカウントであり、ユーザー プロファイルを持っていません。さらに、ASP.NET アプリケーションが偽装を行う場合、Web アプリケーション スレッドは現在の認証済みユーザーとして動作しますが、このユーザーは要求ごとに異なる可能性があります。

このことから、DPAPI を使用する ASP.NET Web アプリケーションでは以下の問題が発生します。

  • 既定の ASPNET アカウントで実行している ASP.NET アプリケーションから DPAPI を呼び出すと、失敗となります。これは、ASPNET アカウントにユーザー プロファイルがないからです。したがって、インタラクティブ ログオンには使用されません。
  • ASP.NET Web アプリケーションが呼び出し元を偽装するように設定すると、ASP.NET アプリケーション スレッドにスレッド偽装トークンが関連付けられます。この偽装トークンに対応するログオン セッションはサーバーで呼び出し元を表すために使用されるネットワーク ログオン セッションです。 ネットワーク ログオン セッションでは、ユーザー プロファイルはロードされません。

この問題を解決するには、DPAPI を呼び出すサービス コンポーネントを アウト プロセス Enterprise Services (COM+) サーバー アプリケーション内に作成します。このコンポーネントを実行するアカウントにはユーザー プロファイルがあり、Win32 サービスを使用してプロファイルを自動的にロードできます。

メモ Win32 サービスを使用する代わりに、サービス コンポーネントに Win32 プロファイル管理関数である LoadUserProfile と UnloadUserProfile の呼び出しを挿入することもできます。

このアプローチには 2 つの欠点があります。1 つは、これらの API を要求ごとに呼び出すと、パフォーマンスが著しく低下するおそれがあることです。もう 1 つは、これらの API を呼び出すコードにはローカル コンピュータの管理特権が必要であり、それは Enterprise Services プロセス アカウントにおける最低限の特権の原則に反することになる点です。

Enterprise Services DPAPI ソリューションを図 12.6 に示します。

図 12.6 ASP.NET Web アプリケーションが COM+ サーバー アプリケーションを使用して DPAPI を呼び出す

図 12.6 に示したソリューションでは、実行時に次のような順序でイベントが発生します。

  1. Windows サービス コントロール マネージャが Win32 サービスを起動し、そのサービスを実行するアカウントのユーザー プロファイルを自動的にロードします。同じ Windows アカウントを Enterprise Services アプリケーションの起動にも使用します。

  2. Win32 サービスがサービス コンポーネントに対して起動メソッドを呼び出し、このメソッドが Enterprise Services アプリケーションを起動し、サービス コンポーネントをロードします。

  3. Web アプリケーションが Web.config ファイルから暗号化された文字列を取得します。

    暗号化した文字列を Web.config に保存するには、次のように <appSettings> 要素を使用します。この要素には、任意のキーと値の組み合わせが指定できます。

<configuration>
 <appSettings>
  <add key="SqlConnString"
       value="AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAABcqc/xCHxki3" />
 </appSettings>
</configuration>

暗号化した文字列を取得するには、次のようなコードを使用します。
string connString = ConfigurationSettings.AppSettings["SqlConnString"];
**メモ** 暗号化した接続文字列の保存には、Web.config と Machine.config のどちらでも使用できます。Machine.config を使用することをお勧めします。このファイルは仮想ディレクトリの外部のシステム ディレクトリにあるからです。この点については、次の「Web.config と Machine.config」で説明します。
  1. アプリケーションがサービス コンポーネントに対して接続文字列を暗号化解除するメソッドを呼び出します。

  2. サービス コンポーネントは、P/Invoke を使用して Win32 DPAPI 関数を呼び出します。

  3. 暗号化した文字列が Web アプリケーションに返されます。

メモ 暗号化した接続文字列を Web.config ファイルに保存する場合は、接続文字列を取得し、サービス コンポーネントの EncryptData メソッドを呼び出して接続文字列を暗号化するユーティリティ アプリケーションを作成します。このユーティリティ アプリケーションを実行するためには、Enterprise Services サーバー アプリケーションを実行するときと同じアカウントでログオンすることが必要です。

ASP.NET から直接 DPAPI を使用する

コンピュータ ストアを使用し、CRYPTPROTECT_LOCAL_MACHINE フラグを指定して DPAPI 関数を呼び出す場合は、ユーザー プロファイルは不要であるため ASP.NET Web アプリケーションから直接、DPAPI 関数を呼び出すことができます。

ただし、コンピュータ ストアを使用するため、コンピュータにログオンできる Windows アカウントであれば、すべて機密情報にアクセスできます。この問題を解決するには、エントロピ パラメータを追加するという方法がありますが、この方法を使用するとキー管理作業が増加します。

エントロピ パラメータを使用する以外に、次のような方法も可能です。

  • データがファイル システムとレジストリのどちらに保存されているかに関係なく、Windows ACL を使用して、暗号化したデータへのアクセスを制限します。
  • エントロピ パラメータをアプリケーションにハードコーディングし、キー管理の問題を回避します。

詳細情報

Web.config と Machine.config

パスワードをプレーン テキストのまま Web.config に保存することはお勧めできません。既定では、Web.config は悪意のあるユーザーがダウンロードしたり表示したりできないように HttpForbiddenHandler で保護されます。しかし、構成ファイルがあるフォルダに直接アクセスできるユーザーであれば、ユーザー名とパスワードを見ることができます。

Machine.config は Web.config より保存場所として安全です。Machine.config は Web アプリケーションの仮想ディレクトリの外部に位置するシステム ディレクトリにあり、ACL で保護されているからです。Machine.config は、必ず ACL で保護してください。

詳細情報

Machine.config のセキュリティの詳細については、「第 8 章 ASP.NET セキュリティ」を参照してください。

UDL ファイルの使用

OLE DB .NET Data Provider では、接続文字列に UDL ファイル名を含めることができます。UDL ファイルは、"File Name=name.udl" という形式で接続文字列に指定します。

重要 このオプションは、OLE DB .NET Data Provider を使用してデータベースに接続する場合のみ有効です。SQL Server .NET Data Provider では、UDL ファイルは使用できません。

UDL ファイルを他のアプリケーション ファイルと一緒に仮想ディレクトリに保存しないでください。UDL ファイルは、Web アプリケーションの仮想ディレクトリ階層の外部に保存し、ファイルまたは保存先のフォルダを Windows ACL で保護することが必要です。また、ファイル正規化やディクショナリ走査のバグから保護するために、UDL ファイルをオペレーティング システムとは別の論理ボリュームに保存することも検討してください。

ACL の細分度

ACL を適用する場合、UDL ファイル またはテキスト ファイルを使用すると、Machine.config と比較して細分度が向上します。Machine.config に対応する既定の ACL では、多様なローカル ユーザーおよびリモート ユーザーにアクセス権を付与できます。たとえば、Machine.config には次のような既定の ACL があります。

MachineName\ASPNET:R
BUILTIN\Users:R
BUILTIN\Power Users:C
BUILTIN\Administrators:F
NT AUTHORITY\SYSTEM:F

これに比べて、アプリケーションの UDL ファイルはさらに詳細な保護が可能です。たとえば、次のように、管理者、システム アカウント、および ASP.NET プロセス アカウント (読み取りアクセス権が必要) へのアクセスを制限できます。ASP.NET プロセス アカウントは読み取りアクセス権が必要です。

BUILTIN\Administrators:F
MachineName\ASPNET:R
NT AUTHORITY\SYSTEM:F  

メモ UDL ファイルは ADO.NET クライアント アプリケーションの外部で変更が可能であるため、接続が確立されるたびに、UDL ファイルの参照を含む接続文字列が解析されます。この処理はパフォーマンスの低下につながります。したがって、最適なパフォーマンスを確保するために、UDL ファイルを含まない静的な接続文字列を使用することをお勧めします。

■ 新しい UDL ファイルを作成するには

  1. Windows エクスプローラを使用して、作成した UDL ファイルを保存するフォルダを開きます。
  2. フォルダ内で右クリックし、[新規作成] をポイントして、[テキスト文書] をクリックします。
  3. ファイル名を入力し、拡張子 .udl を付けます。
  4. この新しいファイルをダブルクリックします。[UDL のプロパティ] ダイアログ ボックスが表示されます。

詳細情報

Microsoft C#® 開発ツール プログラムから UDL ファイルを使用する方法の詳細については、Microsoft サポート技術情報の Knowledge Base の文書 308426「[HOW TO] Visual C# .NET で OleDbConnection オブジェクトを持つデータ リンク ファイルを使用する方法」を参照してください。

カスタム テキスト ファイルの使用

多くのアプリケーションでは、カスタム テキスト ファイルに接続文字列を保存しています。この方法を採用する場合は、以下の点に留意してください。

  • カスタム ファイルは、アプリケーションの仮想ディレクトリ階層の外部に保存します。
  • ファイルの正規化やディレクトリ トラバーサルのバグから保護するために、ファイルをオペレーティング システムとは別の論理ボリュームに保存することも検討してください。
  • アプリケーションのプロセス アカウントに読み取りアクセス権を付与する制限付き ACL でファイルを保護します。
  • 接続文字列をクリア テキストのままファイルに保存しないでください。DPAPI で接続文字列を暗号化したうえで保存することをお勧めします。

レジストリの使用

Windows レジストリのカスタム キーを使用して接続文字列を保存することができます。この情報は、HKEY_LOCAL_MACHINE (HKLM) と HKEY_CURRENT_USER (HKCU) のいずれかのレジストリ ハイブに保存できます。ASPNET アカウントなどのユーザー プロファイルを持たないプロセス ID の場合、この情報は ASP.NET コードで取得できるように HKLM に保存する必要があります。

このアプローチを使用する場合は、以下のことを行ってください。

  • Regedt32.exe を使用し、ACL でレジストリ キーを保護します。
  • データを暗号化してから保存します。

詳細情報

レジストリに保存するデータの暗号化の詳細については、このガイドの「パート IV : 参照」の「暗号化された接続文字列をレジストリに格納する方法」を参照してください。

COM+ カタログの使用

Web アプリケーションにサービス コンポーネントを組み込んでいる場合は、接続文字列を COM+ カタログにコンストラクタ文字列として保存できます。COM+ カタログ内のコンストラクタ文字列は、コンポーネント サービス ツールの使用により管理が容易であり、コンポーネント コードによって簡単に取得できます。Enterprise Services は、オブジェクトをインスタンス化した直後、そのオブジェクトの Construct メソッドを呼び出し、コンストラクション文字列を渡します。

COM+ カタログでは、情報が暗号化されないため、セキュリティ レベルは高くありません。ただし、追加されたプロセス ホップがあるため、構成ファイルよりはセキュリティ レベルが高いと言えます。

コンポーネント サービス ツールを使用して COM+ カタログへのアクセスを禁止するには、アクセスを許可するユーザーの一覧を System アプリケーションの Administrator ロールおよび Reader ロールに追加します。

次に、サービス コンポーネントからオブジェクト コンストラクタ文字列を取得するコードの例を示します。

[ConstructionEnabled(Default="Default Connection String")]
public class YourClass : ServicedComponent 
{
  private string _ConnectionString;
  override protected void Construct(string s) 
  {
    _ConnectionString = s; 
  }
}

セキュリティ レベルを高めるには、コンストラクション文字列を暗号化してから保存し、サービス コンポーネント内で暗号化を解除するコードを追加します。

詳細情報

  • コンストラクション文字列の詳細については、Microsoft サポート技術情報の Knowledge Base の文書 「HOWTO: Access COM+ Object Constructor String in a VB Component」(英語情報) を参照してください。
  • .NET Framework SDK に付属の完全なサンプル コードは、\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Samples\Technologies\ComponentServices\ObjectConstruction にあるサンプル オブジェクト コンストラクタを参照してください。

データベースとの照合によるユーザー認証

ユーザーの資格情報をデータベース ストアとの照合によって検証することが必要なアプリケーションを作成する場合は、以下の点に留意してください。

  • ランダム salt 値を適用し、一方向パスワード ハッシュを保存してください。
  • ユーザーの資格情報を検証するときには SQL コード注入を使用しないでください。

一方向パスワード ハッシュ (salt 値を適用) の保存

フォーム認証を行うアプリケーションでは、多くの場合、パスワードを含むユーザー資格情報をデータベースに保存することが必要になります。セキュリティ上の理由から、クリア テキストのままでも、暗号化した状態であっても、パスワードをデータベースに保存することは避ける必要があります。

暗号化したパスワードを保存するべきでないのは、キー管理の問題が発生するからです。暗号化によってパスワードを保護することはできますが、暗号化キーの保存方法も考える必要があります。キーが簡単に入手できる状態にあれば、データ ストア内のすべてのパスワードが解読可能になります。

望ましいアプローチは次のとおりです。

  • 一方向パスワード ハッシュを保存します。パスワードを検証するときにハッシュを再計算します。
  • パスワード ハッシュに暗号を強化する乱数である salt 値を適用します。パスワード ハッシュに salt 値を適用することによって、ディクショナリ攻撃によって解読される危険が少なくなります。

salt 値の作成

次のコードは、System.Security.Cryptography 名前空間の RNGCryptoServiceProvider クラスが提供する乱数生成機能を使用して salt 値を作成するものです。

public static string CreateSalt(int size)
{
  RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
  byte[] buff = new byte[size];
  rng.GetBytes(buff);
  return Convert.ToBase64String(buff);
}

salt 値を適用したハッシュ値の作成

次のコードは、入力されたパスワードと salt 値からハッシュ値を生成するものです。

public static string CreatePasswordHash(string pwd, string salt)
{
  string saltAndPwd = string.Concat(pwd, salt);
  string hashedPwd = 
        FormsAuthentication.HashPasswordForStoringInConfigFile(
                                             saltAndPwd, "SHA1");
  return hashedPwd;
}

詳細情報

このアプローチの実装に関する詳細については、このガイドの「パート IV : 参照」の「SQL Server 2000 でフォーム認証を使用する方法」を参照してください。

SQL コード注入攻撃

SQL データベースとの照合によるフォーム認証を使用する場合は、ここで説明する方法で SQL コード注入攻撃に対する防御策を講じる必要があります。SQL コード注入攻撃とは、悪質な SQL コードをアプリケーションに送り込むことをいいます。多くの場合、このようなコードはアプリケーション内の正しい SQL コードに追加されます。程度の差はあっても、あらゆる種類の SQL データベースが SQL コード注入攻撃によって被害を受ける可能性がありますが、ここでは SQL Server に的を絞って説明します。

SQL コード注入攻撃に格別の注意を払わなければならないのは、SQL コマンドの要素となるユーザー入力を処理するときです。SQL Server との照合によるフォーム認証など、SQL データベースとの照合によってユーザーを検証する認証方式を使用する場合は、SQL コード注入攻撃に対する防御対策が必要となります。

フィルタ処理していない入力を使用して SQL 文字列を構築する場合、アプリケーションが悪質なユーザー入力を受け取るおそれがあります。ユーザー入力はどのような場合も信頼してはなりません。ユーザー入力を文字列に挿入することによって実行可能ステートメントが完成する場合、悪質なユーザーは、エスケープ文字を使用して正規の SQL ステートメントに SQL コマンドを追加することができます。

以下に示すコードでは、SQL Server に付属の Pubs データベースを使用して、SQL コード注入がどのように行われるかを説明します。

課題

アプリケーションが SQL コード注入攻撃を受ける可能性があるのは、ユーザー入力やその他の未知のデータをデータベース クエリに挿入する場合です。たとえば、次に挙げる 2 つのコードは、いずれも SQL コード注入攻撃の対象となります。

  • フィルタ処理していないユーザー入力を使用して SQL ステートメントを作成します。
SqlDataAdapter myCommand = new SqlDataAdapter(
          "SELECT au_lname, au_fname FROM authors WHERE au_id = '" + 
          Login.Text + "'", myConnection);

  • フィルタ処理していないユーザー入力を含む単一の文字列を作成してストアド プロシージャを呼び出します。
SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure '" + 
                               Login.Text + "'", myConnection);

SQL スクリプト注入攻撃の分析

上で示したように、アプリケーションが、フィルタ処理していないユーザー入力値を受け入れる場合、悪意のあるユーザーはエスケープ文字を使用してコマンドを追加できます。

ユーザーが 172-32-xxxx などの社会保障番号を入力する SQL クエリについて考えてみます。このクエリは次のような形式になります。

SELECT au_lname, au_fname FROM authors WHERE au_id = '172-32-xxxx'

悪意のあるユーザーであれば、次のようなテキストをアプリケーションのテキスト ボックス コントロールなどの入力フィールドに入力することもあります。

' ; INSERT INTO jobs (job_desc, min_lvl, max_lvl) VALUES ('Important Job', 25, 100)  -

この例では、INSERT ステートメントを注入しています。ただし、SQL Server に接続するアカウントに許可されたステートメントは実行されます。上のコードが特に大きな被害を及ぼすのは、アカウントが、xp_cmdshell を使用してシェル コマンドを実行できる sysadmin ロールのメンバであり、SQL Server が他のネットワーク リソースに対するアクセス権を持つドメイン アカウントで実行されている場合です。

上のコマンドによって、次のように SQL 文字列が結合されます。

SELECT au_lname, au_fname FROM authors WHERE au_id =
 '';INSERT INTO jobs (job_desc, min_lvl, max_lvl) VALUES ('Important Job', 25, 100)  --
 

このケースでは、悪質な入力を開始する一重引用符 (') で SQL ステートメントの現在の文字列リテラルが終了しています。この一重引用符は、その後のトークンが現在のステートメントの継続としては意味をなさず、次のステートメントの開始としては意味を持つ場合のみ現在のステートメントを終了させます。

SELECT au_lname, au_fname FROM authors WHERE au_id = ' '

セミコロン (;) は、新しいステートメントの開始を表し、その後に悪質な SQL コードが続いています。

; INSERT INTO jobs (job_desc, min_lvl, max_lvl) VALUES ('Important Job', 25, 100)

メモ このセミコロンは、SQL ステートメントを区切るために必ずしも必要なものではありません。セミコロンが必要かどうかはベンダーや実装によって異なりますが、SQL Server の場合は必要ではありません。たとえば、SQL Server は次のコードを 2 つの独立したステートメントとして解析します。

SELECT * FROM MyTable DELETE FROM MyTable

最後に、2 つのダッシュ (--) は SQL コメントを表し、それ以降のテキストは無視されます。この例では、終了文字の一重引用符 (') が無視されます。このダッシュがなければ SQL 解析エラーとなります。

このようにステートメントが作成された結果、SQL が実行するテキストは次のようになります。

SELECT au_lname, au_fname FROM authors WHERE au_id =
 '' ; INSERT INTO jobs (job_desc, min_lvl, max_lvl) VALUES ('Important Job', 25, 100) --'

解決策

アプリケーションから SQL を呼び出す際のセキュリティ対策としては、次のものがあります。

  • SQL ステートメントを作成するときに Parameters コレクションを使用します。
SqlDataAdapter myCommand = new SqlDataAdapter(
        "SELECT au_lname, au_fname FROM Authors WHERE au_id= @au_id", 
        myConnection);

SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
                                             "@au_id",
                                             SqlDbType.VarChar, 11);
parm.Value= Login.Text;

  • ストアド プロシージャを呼び出すときに Parameters コレクションを使用します。
// AuthorLogin is a stored procedure that accepts a parameter named Login
SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", myConnection);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
                                "@LoginId", SqlDbType.VarChar,11);
parm.Value=Login.Text;

Parameters コレクションを使用すると、悪意のあるユーザーがどのような文字を入力しても、すべてリテラルとして扱われます。Parameters コレクションを使用することには、型と長さがチェックされるという利点もあります。範囲外の値が入力されると、例外が発生します。これは、縦深防御の好例です。
  • フィルタ処理によってユーザー入力から SQL 文字列を抽出します。ここでは、等しい、より小さい、より大きいなどの簡単な SQL 比較ステートメントで使用される文字列リテラルの安全を保証する方法を示します。その方法とは、文字列にアポストロフィが含まれている場合、それをもう 1 つのアポストロフィでエスケープするというものです。SQL 文字列リテラルでは、連続する 2 つのアポストロフィは区切り記号ではなく同じ文字列内にあるアポストロフィのインスタンスと見なされます。
private string SafeSqlLiteral(string inputSQL)
{
  return inputSQL.Replace("'", "''");
}
…
string safeSQL = SafeSqlLiteral(Login.Text);
SqlDataAdapter myCommand = new SqlDataAdapter(
       "SELECT au_lname, au_fname FROM authors WHERE au_id = '" + 
       safeSQL + "'", myConnection);

その他の方法

被害の範囲を限定的なものに抑え、犯行の機会を少なくする方法は他にもあります。ここでは、そうした対策をいくつか紹介します。

  • 入力のサイズと種類を制限することによって、無効な入力をゲート (フロントエンド アプリケーション) で排除します。入力のサイズと種類を制限すると、被害の可能性が飛躍的に減少します。たとえば、データベース検索フィールドが最大長 11 文字で、数字だけで構成される場合は、この条件に合致する入力だけを受け入れます。

  • SQL コードを最低限の特権を持つアカウントで実行します。これによって、被害を受ける可能性が大幅に減少します。

    たとえば、あるユーザーがデータベースからテーブルを削除する SQL (DROP) コードを注入したとしても、SQL 接続のアカウントにテーブルの削除が可能なアクセス許可がなければ、注入された SQL コードは失敗となります。この点も、アプリケーションの SQL 接続に "sa" アカウントやデータベース所有者のアカウントを使用してはならない理由となります。

  • SQL コードで例外が発生したときに、データベースが発行した SQL エラーをエンド ユーザーに対して非提示にします。エラー情報はログ ファイルに記録し、ユーザー向けの情報だけを表示します。これによって、ユーザーにとっては不要で、攻撃に利用される可能性のある情報を公開せずに済みます。

パターン検索ステートメントの保護

'LIKE' 句の文字列リテラルにユーザー入力を使用する場合、アポストロフィ以外にもパターン検索において特別な意味を持つ文字があります。

たとえば、LIKE 句では、"%" は "0 以上の文字と一致する" という意味になります。このような文字がユーザー入力に含まれている場合、特別な意味を持たないリテラルとして扱うためには、その文字をエスケープする必要があります。このような文字に対して特別な扱いをしないと、クエリが誤った結果を返す可能性があります。エスケープされていないパターン検索文字が文字列の先頭またはその付近にあると、インデックスの作成が失敗することもあります。

SQL Server では、次の方法を使用して入力の有効性を保証する必要があります。

private string SafeSqlLikeClauseLiteral(string inputSQL)
{
  // Make the following replacements:
  // '  becomes  ''
  // [  becomes  [[]
  // %  becomes  [%]
  // _  becomes  [_]

  string s = inputSQL;
  s = inputSQL.Replace("'", "''");
  s = s.Replace("[", "[[]");
  s = s.Replace("%", "[%]");
  s = s.Replace("_", "[_]");
  return s;
}  

監査

SQL Server では、ログオンの監査は既定で有効に設定されません。監査を有効にするには、SQL Server Enterprise Manager またはレジストリを使用します。図 12.7 のダイアログ ボックスでは、ユーザー ログオンの、成功、失敗のいずれについても監査を有効に設定しています。

ログ エントリは SQL ログ ファイルに書き込まれます。このファイルの既定の場所は、C:\Program Files\Microsoft SQL Server\MSSQL\LOG です。SQL ログ ファイルは、メモ帳など、どのようなテキスト リーダーでも表示できます。

図 12.7 [SQL Server のプロパティ] ダイアログ ボックスでの監査レベル設定

SQL Server の監査機能はレジストリでも有効にできます。SQL Server の監査機能を有効に設定するには、レジストリ内に次のような AuditLevel キーを作成し、後に示すいずれかの REG_DWORD 値に設定します。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\AuditLevel

このキーの値は次の 4 つの中から選択できます。これらの値によって、監査の詳細度を設定できます。

3 ログインの成功と失敗を両方とも記録します。

2 失敗したログインだけを記録します。

1 成功したログインだけを記録します。

0 ログインをまったく記録しません。

失敗したログインを記録することをお勧めします。この記録は、SQL Server が攻撃を受けているかどうかを判断する材料となります。失敗したログインを記録することによるパフォーマンスの低下は、攻撃を受けていない限りわずかなものでしかなく、攻撃を受けたときには失敗したログインの記録を調べることが必要になります。

SQL DMO (分散管理オブジェクト) に対するスクリプトを作成することもできます。次に、サンプル VBScript コードを示します。

Sub SetAuditLevel(Server As String, NewAuditLevel As SQLDMO_AUDIT_TYPE)
    Dim objServer As New SQLServer2  
    objServer.LoginSecure = True     'Use integrated security
    objServer.Connect Server        'Connect to the target SQL Server
    'Set the audit level
    objServer.IntegratedSecurity.AuditLevel = NewAuditLevel       
    Set objServer = Nothing
End Sub

「SQL Server Books Online」には、列挙型 SQLDMO_AUDIT_TYPE のメンバが次のように記載されています。

SQLDMOAudit_All      3  Log all authentication attempts regardless of success
                        or failure 
SQLDMOAudit_Failure  2  Log failed authentication 
SQLDMOAudit_Success  1  Log successful authentication
SQLDMOAudit_None     0  Do not log authentication attempts

SQL Server のプロセス ID

SQL Server は、最低限の特権を持つドメイン アカウントで実行します。SQL Server をインストールするときには、SQL Server サービスをローカル SYSTEM アカウントと指定アカウントのどちらで実行するのかを選択できます。

SYSTEM アカウントや管理者アカウントは使用しないでください。必ず最低限の特権を持つドメイン アカウントを使用してください。このアカウントに特権を与える必要はありません。インストール プロセス (インストール後に SQL Server を再設定する場合は SQL Server Enterprise Manager) において、指定したアカウントに必要な特権が付与されるからです。

まとめ

.NET Web アプリケーションにおけるデータ アクセス セキュリティ対策の要点は以下のとおりです。

  • SQL Server に対する認証には可能な限り Windows 認証を使用します。
  • データベースの操作について最低限の特権を持つアカウントを使用します。
  • SQL Server に接続するときには、ASP.NET/Enterprise Services の実行アカウントとして最低限の特権を持つローカル アカウントを使用します。
    • SQL 認証を使用する場合は、セキュリティを強化するために次のような対策をとります。
    • 強力なパスワードを持つカスタム アカウントを使用します。
    • 各アカウントの SQL Server におけるアクセス許可をデータベース ロールによって制限します。
    • 接続文字列を保存するファイルに ACL を追加します。
    • 接続文字列を暗号化します。
    • 資格情報を保存するときに DPAPI を適用します。

認証方法として SQL との照合によるフォーム認証を使用する場合は、SQL コード注入攻撃に対する防御策を講じます。

ユーザーのパスワードはユーザー検証に使用するデータベースには保存しません。クリア テキストや暗号化したパスワードではなく salt 値を適用したパスワード ハッシュを保存します。

ネットワークを介して SQL Server との間で送信する機密データを保護します。

  • Windows 認証では、資格情報は保護できますが、アプリケーション データは保護できません。
  • IPSec または SSL を使用します。

patterns and practices home