このトピックはまだ評価されていません - このトピックを評価する

セキュリティ保護されたデータ アクセスを構築する

セキュリティ保護されたデータ アクセスを構築する

公開日: 2004年9月7日 | 最終更新日: 2004年9月7日
トピック

 モジュールの内容
 目的
 適用対象
 モジュールの使用方法
 脅威とその対策
 設計に関する考慮事項
 入力検証
 SQL インジェクション
 認証
 承認
 構成管理
 機密性の高いデータ
 例外管理
 セキュリティ保護されたデータ アクセス コンポーネントを構築する
 コード アクセス セキュリティに関する考慮事項
 展開に関する考慮事項
 要約
 その他のリソース

モジュールの内容

データ アクセスとは、いずれかの使用可能な ADO.NET データ プロバイダを使用して ASP.NET Web アプリケーションからデータベースにアクセスするプロセスのことです。

このデータベースは、アプリケーション レベルの攻撃の最大の標的になります。アプリケーション レベルの攻撃は、データ アクセス コードの脆弱性を悪用してデータベースに不正にアクセスすることを目的として行われます。ポート 80 以外の攻撃経路がすべて閉じられている場合、アプリケーションへの入り口であるポート 80 が、攻撃者がデータの盗み出し、操作、および破壊に利用する経路になります。

このモジュールでは、セキュリティの高いデータ アクセス コードを作成して一般的な脆弱性や危険を回避する方法について説明します。また、データ アクセス コード内で使用すると主なデータ アクセス関連の脅威を軽減することができる、一連の対策と防御策を紹介します。

目的

このモジュールの目的は次のとおりです。

  • セキュリティの高いデータ アクセス コードを設計、作成、および展開する。

  • コード アクセス セキュリティとロール ベース セキュリティを使用して、不正な呼び出し元やコードによるアクセスを制限する。

  • ユーザーを安全に認証する。

  • SQL インジェクション攻撃を防止する。

  • データベース接続文字列をセキュリティ保護する。

  • データベースに保存されるデータを暗号化により保護する。

  • データベースとの間でネットワーク経由で送受信されるデータをセキュリティ保護する。

  • (ハッシュを salt 処理して) パスワードをデータベースに安全に保存する。

  • セキュリティの高い例外処理を実装する。

  • コード アクセス セキュリティを使用して、中程度の信頼が与えられている Web アプリケーションから (完全な信頼を要求する) OLE DB 用、Oracle 用、および ODBC 用のデータ プロバイダを使用できるようにする方法を学習する。

  • 一般的なデータ アクセス関連の脅威 (SQL インジェクション、構成データの漏えい、機密性の高いアプリケーション データの漏えい、データベース スキーマとデータベース接続の詳細の漏えい、権限のないアクセス、ネットワーク盗聴など) を解消するにはどの対策を適用すればよいかを学習する。

適用対象

このモジュールは、次の製品および技術に適用されます。

  • Microsoft® Windows® 2000 Server および Microsoft Windows Server™ 2003

  • Microsoft .NET Framework 1.1 および ASP.NET 1.1

  • Microsoft SQL Server

モジュールの使用方法

このモジュールを最大限に活用するには、このモジュールを使用する前に、またはこのモジュールと一緒に、次のモジュールを読む必要があります。

  • モジュール 2「脅威とその対策」を参照してください。 Web アプリケーションが遭遇する可能性がある脅威とその対策についての広く深い知識が得られます。

  • モジュール 4「セキュリティ保護された Web アプリケーションの設計ガイドライン」を参照してください。セキュリティ保護されたソリューションを構築するうえでのアーキテクチャと設計に関する課題事項とガイドラインについて学習することができます。

  • モジュール 18「データベース サーバーをセキュリティ保護する」を参照してください。データベース サーバーをセキュリティ保護する方法について理解することができます。

  • モジュール 7「セキュリティ保護されたアセンブリを構築する」を参照してください。モジュール 7 に記載されている、セキュリティ保護されたアセンブリの構築とセキュリティの高いマネージ コードの開発のためのガイドラインと推奨事項も、データ アクセス コードに適用する必要があります。

  • 評価関連のモジュールを使用してください。モジュール 5「セキュリティのためのアーキテクチャと設計レビュー」、モジュール 21「コード レビュー」、およびモジュール 22「展開レビュー」の Web サービスに関するセクションを参照して、製品サイクルのさまざまな段階でデータ アクセスのセキュリティのレビューを行う必要があります。

  • チェックリストを使用してください。このガイドの「チェックリスト」セクションにある「チェックリスト: データ アクセスをセキュリティ保護する」には、容易に確認できるチェックリストが掲載されています。このタスクベースのチェックリストは、このモジュールの推奨事項のまとめとして使用できます。

.NET Framework の最新バージョン (1.1) では、部分的に信頼されている呼び出し元をサポートし、部分的に信頼されている Web アプリケーションで安全に使用できるのは、SQL Server 用の ADO.NET データ アクセス プロバイダのみであることに注意してください。OLE DB 用、Oracle 用、および ODBC 用の ADO.NET データ プロバイダは完全な信頼を要求します。

脅威とその対策

セキュリティの高いデータ アクセス コードを作成するには、どのような脅威があるのか、一般的な脆弱性はデータ アクセス コード上どのように現れるのか、およびリスク軽減のために適切な対策を適用するにはどのようにするのかを理解する必要があります。

データ アクセス コードに対する主な脅威は次のとおりです。

  • SQL インジェクション

  • 構成データの漏えい

  • 機密性の高いアプリケーション データの漏えい

  • データベース スキーマおよびデータベース接続の詳細の漏えい

  • 権限のないアクセス

  • ネットワーク盗聴

図 14.1 は、これらの主な脅威を示しています。

データ アクセス コードに対する脅威と攻撃

図 14.1
データ アクセス コードに対する脅威と攻撃

SQL インジェクション

SQL インジェクション攻撃とは、脆弱性があるデータ アクセス コードを悪用して、データベースで任意のコマンドを実行できるようにする攻撃のことです。アプリケーションで無制約のアカウントが使用されている場合、攻撃者がクエリやコマンドを実行できる余地が大きくなるので、脅威がいっそう高まります。

脆弱性

データ アクセス コードが SQL インジェクション攻撃の影響を受ける原因となる脆弱性のうち、一般的なものは次のとおりです。

  • 脆弱な入力検証。

  • タイプ セーフなパラメータを使用せずに SQL ステートメントの動的構成が行われている。

  • 特権が過剰に与えられたデータベース ログインを使用している。

対策

SQL インジェクション攻撃に対抗するには、次のことを行います。

  • 入力データに制約を課し、不正な入力データを排除します。

  • タイプ セーフな SQL パラメータをデータ アクセスに使用します。このパラメータは、ストアド プロシージャや動的に構成される SQL コマンド文字列と共に使用できます。この対策を行うと、パラメータの型と長さの確認が可能になります。また、挿入されたコードが実行可能なステートメントとしてではなくリテラル データとしてデータベースで処理されるようにすることができます。

  • データベースで限られた権限のみを持つアカウントを使用します。理想的には、特定のストアド プロシージャに対する実行権限のみをアカウントに与えます。テーブルを直接操作できる権限を与えないでください。

構成データの漏えい

データ アクセス コードで使用される構成データのうち、機密性が最も高いのは、データベース接続文字列です。侵害された接続文字列にユーザー名とパスワードが含まれていると、その影響はかなり重大なものになります。

脆弱性

次のような脆弱性が存在すると、構成データの侵害により生じるセキュリティ上のリスクが増大します。

  • 接続文字列に資格情報が指定されていることが求められる、SQL 認証が使用されている。

  • コードに接続文字列が埋め込まれている。

  • 構成ファイルに接続文字列がクリア テキストで含まれている。

  • 接続文字列を暗号化していない。

対策

構成データの漏えいを防止するには、次のことを行います。

  • Windows 認証を使用することにより、接続文字列に資格情報が含まれないようにします。

  • 接続文字列を暗号化し、この暗号化されたデータへのアクセスを制限します。

機密性の高いアプリケーション データの漏えい

多くのアプリケーションは、顧客のクレジット カード番号などの機密性の高いデータを保存します。この種のデータについては、プライバシーと完全性の保護が不可欠です。

脆弱性

コーディング手法のうち、機密性の高いアプリケーション データの漏えいにつながる可能性があるものは次のとおりです。

  • データを暗号化せずに保存する。

  • 脆弱な承認を使用する。

  • 脆弱な暗号化を使用する。

対策

機密性の高いアプリケーション データの漏えいを防止するには、次のことを行います。

  • 強力な暗号化を使用して、データをセキュリティ保護します。

  • データ アクセスが実行される前に呼び出し元の承認が行われるようにして、ユーザーが各自のデータにしかアクセスできないようにします。

データベース スキーマおよびデータベース接続の詳細の漏えい

コード上、例外の詳細をクライアントに返すようにしている場合、悪意のユーザーがその情報を使用してサーバーを攻撃する可能性があります。データ アクセス コードでの例外は、データベース スキーマの詳細、データ ストアの特徴、SQL コードの一部などの機密性の高い情報の漏えいにつながることがあります。

脆弱性

次のような脆弱性が存在すると、情報の漏えいが発生することがあります。

  • 不適切な例外処理。

  • ASP.NET の構成が脆弱であるため、未処理の例外の詳細がクライアントに返される。

対策

上記のような情報の漏えいを防止するには、次のことを行います。

  • データ アクセス コードで、データ アクセスの例外のキャッチ、ログ記録、および処理を行うようにします。

  • 呼び出し元には汎用エラー メッセージが返されるようにします。このためには、Web.config 構成ファイルまたは Machine.config 構成ファイルの <customErrors> 要素を適切に構成することが必要になります。

権限のないアクセス

承認が適切に行われていない場合、ユーザーが別のユーザーのデータを参照できるようになったり、その他のアクセスが制限されているデータにアクセスできるようになったりします。

脆弱性

権限のないアクセスを許す可能性がある手法は、次のとおりです。

  • データ アクセス コードに承認機能がなく、アクセスを無制限で許可している。

  • データベース アカウントに過剰に特権を与えている。

対策

権限のないアクセスを防止するには、次のことを行います。

  • プリンシパル アクセス許可要求を使用して呼び出し元ユーザーの承認を行います。

  • コード アクセス セキュリティのアクセス許可要求を使用して呼び出し元ユーザーの承認を行います。

  • 使用する権限を限定することにより、データベースへのアプリケーションのログインを制限し、テーブルへの直接アクセスを防止します。

ネットワーク盗聴

ほとんどのアプリケーションの展開アーキテクチャでは、データ アクセス コードがデータベース サーバーから物理的に分離されます。その結果、アプリケーション固有のデータやデータベースへのログインに必要な資格情報などの機密性の高いデータを、ネットワーク盗聴から保護することが必要になります。

脆弱性

次のような手法を使用すると、ネットワーク盗聴に対する脆弱性が増大します。

  • SQL 認証の際に、クリア テキストの資格情報をネットワーク経由で送信する。

  • 機密性の高いアプリケーション データを暗号化せずにデータベース サーバーとの間で送受信する。

対策

ネットワーク盗聴に対する脆弱性を抑制するには、次のことを行います。

  • Windows 認証を使用することにより、資格情報がネットワーク経由で送信されるのを回避します。

  • データベース サーバーにサーバー証明書をインストールします。これにより、ネットワーク経由で送信される SQL 資格情報が自動的に暗号化されるようになります。

  • SSL 接続を Web サーバーとデータベース サーバーの間に使用して、機密性の高いアプリケーション データを保護します。これを行うには、データベース サーバーの証明書が必要です。

  • IPSec で暗号化したチャネルを Web サーバーとデータベース サーバーの間に使用します。

設計に関する考慮事項

コードの作成を開始する前に、いくつかの重要な事項を設計の時点で考慮する必要があります。主な考慮事項は次のとおりです。

  • Windows 認証を使用する。

  • 最小権限のアカウントを使用する。

  • ストアド プロシージャを使用する。

  • ストレージ内の機密性の高いデータを保護する。

  • 独立したデータ アクセス アセンブリを使用する。

Windows 認証を使用する

理想的には、Windows 認証を使用するように設計します。これにより、セキュリティ上の利点が得られます。Windows 認証を使用すると、資格情報を埋め込んだデータベース接続文字列を保存する必要がなくなります。また、資格情報がネットワーク経由で送信されなくなります。さらに、アカウントとパスワードを対象としたセキュリティの高い管理ポリシーを利用することができます。ただし、Windows 認証を使用する場合には、どのアカウントを使用して SQL Server に接続するかを慎重に検討する必要があります。

詳細については、このモジュールで後述する「認証」を参照してください。

最小権限のアカウントを使用する

アプリケーションが使用するアカウントは、データベースで限られた権限のみを持つ、最小権限のアカウントにする必要があります。データベースへのアプリケーションのログインは、必ず適切に承認され、制限されるようにします。詳細については、このモジュールで後述する「承認」を参照してください。

最小権限のアカウントを使用すると、リスクが軽減されます。また、アカウントが侵害された場合や悪意のコードが挿入された場合に受ける損害が抑えられます。SQL インジェクションが行われた場合、挿入されたコマンドは、アプリケーション ログインごとに定義されているセキュリティ コンテキストで実行されます (アプリケーション ログインに関連付けられているデータベース上の権限に応じて実行されます)。特権が過剰に与えられているアカウント (SQL Server の sysadmin ロールのメンバなど) を接続に使用している場合、攻撃者はサーバー上の任意のデータベースで任意の操作を実行することができます。これには、データの挿入、更新、削除、テーブルの削除、およびオペレーティング システムのコマンドの実行が含まれます。

重要: SQL Server への接続には、sa アカウントや、SQL Server の sysadmin ロールまたは db_owner ロールのメンバとなっているアカウントを使用しないでください。

ストアド プロシージャを使用する

ストアド プロシージャを使用すると、パフォーマンス上、保守上、およびセキュリティ上の利点が得られます。データ アクセスには、パラメータ化されたストアド プロシージャを可能な限り使用してください。セキュリティ上の利点は、次のとおりです。

  • データベースへのアプリケーションのログインを制限して、特定のストアド プロシージャを実行する権限のみが与えられるようにすることができます。テーブルへの直接アクセスを許可する必要はありません。このような対応は、SQL インジェクション攻撃のリスクの軽減に効果的です。

  • ストアド プロシージャに渡される入力データのすべてについて長さと型の確認が行われます。また、パラメータは実行可能なコードとして処理されません。これもまた、SQL インジェクションによるリスクを軽減します。

何らかの理由によりパラメータ化されたストアド プロシージャを使用することができず、SQL ステートメントを動的に構成する必要がある場合には、入力データの長さと型の確認が行われるようにするため、型指定されたパラメータとパラメータ プレースホルダを使用してステートメントの動的構成を行うようにします。

ストレージ内の機密性の高いデータを保護する

保存されているデータのうち、プライバシーと完全性の保証が求められるものを確認してください。パスワードの検証のためだけにパスワードをデータベースに保存している場合は、一方向ハッシュの使用を検討します。パスワードのハッシュのテーブルが侵害されても、格納されているハッシュからクリア テキストのパスワードが解読されることはありません。

クレジット カード番号などのユーザーから提供された機密性の高いデータを保存する場合には、Triple DES (3DES) などの強力な対称暗号化アルゴリズムを使用してそのデータを暗号化します。3DES 暗号化キーについては、Win32 データ保護 API (DPAPI) を使用して暗号化します。暗号化されたキーは、管理者とアプリケーション プロセス アカウントのみが使用できるように ACL で制限したレジストリ キーに保存します。

DPAPI を使用しない理由

DPAPI は、接続文字列やアカウントの資格情報などの、コンピュータが故障したときに手動で復元や再構築を行うことができる秘密情報を暗号化する場合にはお勧めできる方法ですが、クレジット カード番号などの保存には適していません。これは、復元性の問題 (キーが失われると暗号化されたデータを復元する方法がなくなる問題) や Web ファームの問題があるためです。代わりに、3DES などの対称暗号化アルゴリズムを適用し、暗号化キーを DPAPI で暗号化してください。

機密性の高いデータをデータベースに保存する場合に DPAPI が適していないのは、主に次のような問題があるためです。

  • DPAPI をコンピュータ キーと共に使用する場合、CRYPTPROTECT_LOCAL_MACHINE を CryptProtectData 関数または CryptUnprotectData 関数に渡して、コンピュータ アカウントに対応した暗号化キーを生成します。つまり、Web ファーム内の各サーバーがそれぞれ異なるキーを持つことになり、各サーバーは別のサーバーで暗号化されたデータにアクセスすることができません。また、Web サーバーのコンピュータが破損すると、キーが失われるため、データベースにある暗号化されたデータを復元することができなくなります。

  • コンピュータ キーを使用するアプローチでは、データを暗号化したコンピュータのユーザーであれば誰でもそのデータの暗号化を解除することができます (別の暗号化メカニズムを追加で適用している場合を除きます)。

  • DPAPI をユーザー キーと共に使用する場合、ローカル ユーザー アカウントを利用していると、Web サーバーとローカル アカウントの組み合わせごとに異なるセキュリティ識別子 (SID) とキーが生成されるため、各サーバーは別のサーバーで暗号化されたデータにアクセスすることができません。

  • DPAPI をユーザー キーと共に使用する場合、Web ファーム内のすべてのコンピュータにまたがって移動ユーザー プロファイルを利用していると、ユーザー アカウントが同じである限り、すべてのデータで暗号化と復号化のキーが同じになります。しかし、移動ユーザー プロファイル アカウントを管理するドメイン コントローラが損傷したり、破壊されたりすると、同じ SID を持つユーザー アカウントを再作成することは不可能なので、データベースにある暗号化されたデータを復元することができなくなります。

    また、移動ユーザー プロファイルが利用されている場合、攻撃者は、データの取得に成功したときにはネットワーク上のどのコンピュータでもそのデータの暗号化を解除することができます (ただし、これは攻撃者がそのデータの暗号化に利用されたユーザー アカウントでコードを実行できることが前提になります)。この結果、攻撃を受ける可能性がある領域が広がります。そのため、この手法はお勧めできません。

独立したデータ アクセス アセンブリを使用する

データ アクセス ロジックを ASP.NET ページや分離コード ファイルに直接配置することは、できるだけ避けるようにします。データ アクセス ロジックを独立したアセンブリに配置して、アプリケーションのビジネス ロジックやプレゼンテーション ロジックから分離された論理的なデータ アクセス層を実装すると、セキュリティ上、再利用上、および保守上の利点が得られます。

セキュリティの観点からは、次のことを行うことができます。

  • アセンブリに対して厳密名を指定すること。これは改ざん対策になります。

  • サンドボックスを使用してデータ アクセス コードを隔離すること。これは、部分的に信頼されている呼び出し元 (部分的に信頼されている Web アプリケーションなど) をサポートする必要がある場合に重要です。

  • コード ID アクセス許可要求を使用して呼び出し元コードの承認を行うデータ アクセス メソッドとデータ アクセス クラスを使用すること。

防御を多層化するため、図 14.2 に示すように、プリンシパル アクセス許可要求を使用したプリンシパル ベースの承認をビジネス コンポーネントで実行すると共に、コード ID アクセス許可要求を使用して、データ アクセス ロジックを呼び出すコードの承認を行うようにしてください。

プレゼンテーション層、ビジネス層、およびデータ アクセス層の分離

図 14.2
プレゼンテーション層、ビジネス層、およびデータ アクセス層の分離

データ アクセス コードでの承認の詳細については、このモジュールで後述する「承認」を参照してください。

入力検証

ビジネスの観点ではデータベース内に有効で一貫性のあるデータを維持することが求められていますが、これとは別に、SQL インジェクションの防止のため、データをデータベースに送信する前に検証することも必要とされています。データ アクセス コードは、現在の信頼の境界の内部にある別のコンポーネントから入力を受け取った場合、その入力データが既に検証されていることがわかっていれば (ASP.NET の Web ページやビジネス コンポーネントによって検証されているときなど)、詳細なデータ検証を行いません。ただし、データ アクセス コードでは必ず SQL パラメータを使用してください。SQL パラメータを使用する際には入力されたパラメータの型と長さを検証することができます。次のセクションでは、SQL パラメータの使用方法について説明します。

SQL インジェクション

SQL インジェクション攻撃が発生する可能性があるのは、アプリケーションが、入力された情報を使用して、データベースにアクセスするための動的 SQL ステートメントを構成している場合です。また、コードでストアド プロシージャを使用しており、そのストアド プロシージャに渡される文字列にフィルタ処理されていないユーザー入力が含まれる場合にも、この攻撃が発生する可能性があります。SQL インジェクションの結果、攻撃者が、アプリケーション ログインを使用して、データベースでコマンドを実行できるようになることがあります。特権を過剰に与えられたアカウントがアプリケーションからデータベースへの接続に使用されている場合、この問題の影響は重大です。

注: SQL インジェクション攻撃は、SSL や IPSec などの従来のセキュリティ対策では防ぐことができません。

SQL インジェクションを防止する

SQL インジェクション攻撃を防止するには、次の対策を行います。

  • 入力を制限します。

  • タイプ セーフな SQL パラメータを使用します。

入力を制限する

入力の型、長さ、書式、および範囲の検証を行います。数値が想定されていない場合は、数値を禁止します。入力元についても考慮する必要があります。十分な入力検証を既に行っていることがわかっている信頼された入力元からのデータについては、データ アクセス コードでの検証を省略してもかまいません。信頼されていない入力元からデータが提供された場合や、防御を多層化する場合には、データ アクセス用のメソッドとコンポーネントで入力を検証する必要があります。

タイプ セーフな SQL パラメータを使用する

SQL の Parameters コレクションは、型を確認する機能と長さを検証する機能を提供します。Parameters コレクションを使用すると、入力はリテラル値として処理されます。SQL 上、その入力が実行可能なコードとして処理されることはありません。Parameters コレクションを使用することのもう 1 つの利点は、型と長さの確認を強制的に実施できることです。定められた範囲にない値が入力された場合には、例外が発生します。これは防御の多層化の好例です。

重要: SQL インジェクションは、SSL では防ぐことができません。データベースにアクセスするときに、適切な入力検証を実行せず、適切なデータ アクセス技術を使用しないアプリケーションは、SQL インジェクション攻撃を受けやすくなっています。

できる限りストアド プロシージャを使用するようにすると共に、Parameters コレクションを使用してストアド プロシージャを呼び出すようにしてください。

Parameters コレクションをストアド プロシージャと共に使用する

次のコードは、Parameters コレクションの使用例を示しています。

SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", conn);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
"@au_id", SqlDbType.VarChar, 11);
parm.Value = Login.Text;

この例では、@au_id パラメータは、実行可能なコードとしてではなくリテラル値として処理されます。また、このパラメータの型と長さが確認されています。この例の場合、入力値は 11 文字を超えることができません。入力データがパラメータで指定されている型や長さに適合していない場合には、例外が生成されます。

ストアド プロシージャを使用しても、SQL インジェクションを防止できるとは限らないことに注意してください。重要なのは、ストアド プロシージャと共にパラメータを使用することです。パラメータを使用しておらず、フィルタ処理されていない入力をストアド プロシージャで使用している場合、ストアド プロシージャは SQL インジェクションの影響を受けやすくなっています。たとえば、次のようなコードは脆弱です。

SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure '" + 
Login.Text + "'", conn);

重要: ストアド プロシージャを使用するときは、必ずパラメータを使用してください。

Parameters コレクションを動的 SQL と共に使用する

ストアド プロシージャを使用できない場合にも、次のコードが示しているような方法で、パラメータを使用することができます。

SqlDataAdapter myCommand = new SqlDataAdapter(
"SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", conn);
SqlParameter parm = myCommand.SelectCommand.Parameters.Add("@au_id", 
SqlDbType.VarChar, 11);
parm.Value = Login.Text;

一括送信されるステートメントでパラメータを使用する

サーバーに SQL ステートメントを一括送信するために複数のステートメントを連結する場合、一般的にパラメータは使用できないと誤解されています。実際には、同じパラメータ名を繰り返し使用しないようにすれば、このような場合にもパラメータを使用することができます。SQL テキストを連結するときに各パラメータ名に番号などの一意な値を付加すれば、パラメータの繰り返しを容易に防ぐことができます。

フィルタ ルーチンを使用する

SQL インジェクション攻撃の防止のために使用されている別のアプローチは、フィルタ ルーチンを開発して、SQL 上特別な意味を持つ文字 (単一のアポストロフィなど) にエスケープ文字を追加するというアプローチです。次のコードは、エスケープ文字を追加するフィルタ ルーチンの例を示しています。

private string SafeSqlLiteral(string inputSQL)
{
return inputSQL.Replace("'", "''");
}

このようなルーチンの問題点として挙げられ、かつこのようなルーチンを完全に信用してはいけない理由となっている点は、攻撃者が 16 進表現の ASCII 文字を使用すると、ルーチンによる文字の確認を回避できることです。このような問題はありますが、防御の多層化のための方策の一部として、入力フィルタを使用することをお勧めします。

注: 入力フィルタに依存しないようにしてください。

LIKE 句を使用する

LIKE 句を使用している場合には、ワイルドカード文字にもエスケープ文字の追加が必要になることに注意してください。次のコードは、このような場合に適した手法の例を示しています。

s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");

認証

アプリケーションから SQL Server のデータベースに接続する場合には、Windows 認証と SQL 認証のどちらを使用するかを選択することができます。Windows 認証の方がセキュリティ上優れています。データベースへの接続に複数の異なるアカウントを使用する必要があり、かつ LogonUser の呼び出しを回避する必要があるなどの理由により、SQL 認証を使用しなければならない場合には、そのために増加するリスクをできる限り軽減するための対策を追加するようにします。

注: LogonUser を使用して偽装トークンを作成する場合、Microsoft Windows 2000 の強力な特権である "オペレーティング システムの一部として機能" 特権が必要になります。そのため、このアプローチは避ける必要があります。

次の推奨事項について検討してください。

  • Windows 認証を使用します。

  • SQL 認証を使用する場合は資格情報を保護します。

  • 最小権限のアカウントを使用して接続します。

Windows 認証を使用する

Windows 認証は、資格情報をネットワーク経由で送信しません。Web アプリケーション用に Windows 認証を使用する場合、サービス アカウントやプロセス アカウント (ASPNET など) を使用してデータベースに接続するケースがほとんどです。データベース サーバーで使用するアカウントは、Windows と SQL Server の両方で認識されるものにする必要があります。このアカウントには SQL Server へのログインが許可されていなければならず、このログインにはデータベースにアクセスする権限が関連付けられている必要があります。

Windows 認証を使用する場合は、信頼された接続を使用することになります。以下のコードは、Windows 認証を使用する場合の典型的な接続文字列を示しています。

SQL Server 用の ADO.NET データ プロバイダを使用する場合の例

SqlConnection pubsConn = new SqlConnection(
"server=dbserver; database=pubs; Integrated Security=SSPI;");

OLE DB データ ソース用の ADO.NET データ プロバイダを使用する場合の例

OleDbConnection pubsConn = new OleDbConnection(
"Provider=SQLOLEDB; Data Source=dbserver; Integrated Security=SSPI;" +
"Initial Catalog=northwind");

SQL 認証を使用する場合に資格情報を保護する

SQL 認証を使用しなければならない場合は、資格情報がクリア テキストでネットワークに送信されないようにします。また、データベース接続文字列に資格情報が含まれるので、接続文字列を暗号化する必要があります。

ネットワーク経由で送信される資格情報を自動的に暗号化できるようにする場合は、サーバー証明書をデータベース サーバーにインストールします。また、データベース サーバーとの間で送受信するトラフィックのすべてをセキュリティ保護する場合は、IPSec で暗号化したチャネルを Web サーバーとデータベース サーバーの間に使用します。接続文字列のセキュリティ保護には、DPAPI を使用します。詳細については、このモジュールで後述する「構成管理」の「接続文字列をセキュリティ保護する」を参照してください。

最小権限のアカウントを使用して接続する

アプリケーションからデータベースへの接続には、最小権限のアカウントを使用するようにします。Windows 認証を接続に使用する場合、使用する Windows アカウントは、オペレーティング システムから見て特権が最小限になっており、Windows のリソースにアクセスするための特権と能力が制限されているものでなければなりません。また、Windows 認証と SQL 認証のどちらを使用するかに関係なく、対応する SQL Server ログインには、データベース上の権限に基づく制限が設けられている必要があります。

最小権限のデータベース アカウントを作成する方法の詳細、および ASP.NET Web アプリケーションからリモート データベースへの接続に Windows 認証を使用する場合のオプションの詳細については、モジュール 19「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」の「データ アクセス」を参照してください。

承認

承認プロセスは、ユーザーがデータの取得と操作を実行できるかどうかを特定のデータに関して確定するプロセスです。データ アクセス コードでは、要求された操作の実行可否を決定する目的で承認を使用できます。データベースでは、アプリケーションが使用する SQL ログインの能力を制限する目的で承認を実行できます。このように、承認には 2 つのアプローチがあります。

承認を適切に行わなければ、あるユーザーが別のユーザーのデータを参照できるようになったり、アクセスが制限されているデータに不正なユーザーがアクセスできるようになったりします。このような脅威を解消するには、次のことを行います。

  • 不正な呼び出し元を制限します。

  • 不正なコードを制限します。

  • データベース内でアプリケーションを制限します。

図 14.3 は、使用する必要がある承認ポイントと承認手法をまとめたものです。

データ アクセスに関わる承認、アセンブリ、およびデータベース

図 14.3
データ アクセスに関わる承認、アセンブリ、およびデータベース

データ アクセス コードでは呼び出し元ユーザーや呼び出し元コードの承認にアクセス許可要求をどのように使用できるのかに注意する必要があります。コード ID 要求は .NET コード アクセス セキュリティの機能です。

データベースでアプリケーションを承認するときには、特定のストアド プロシージャに対する実行権限のみを与えられている、最小権限のSQL Server ログインを使用します。特別な理由がある場合を除き、作成、取得、更新、および破棄/削除 (CRUD) の各操作をテーブルに直接行うことを、アプリケーションに対して承認しないでください。

注: ストアド プロシージャはデータベース システムのセキュリティ コンテキストで実行されます。アプリケーションに特定のストアド プロシージャに対する権限を割り当てると、アプリケーションが実行できる論理操作を制限することはできますが、そのストアド プロシージャによって実行される操作の結果を制限することはできません。ストアド プロシージャは信頼されたコードです。ストアド プロシージャへのインターフェイスは、データベース上の権限を使用してセキュリティ保護する必要があります。

不正な呼び出し元を制限する

データ アクセス コードでは、データベースに接続する前に、ロールまたは ID に基づいてユーザーを承認する必要があります。通常は、アプリケーションのビジネス ロジックにロール確認を組み込みます。ただし、ビジネス ロジックとデータ アクセス ロジックが明確に区別されていない場合には、データベースへのアクセスを実行するメソッドでプリンシパル アクセス許可要求を使用します。

次に示す属性では、Manager ロールのメンバであるユーザーのみが DisplayCustomerInfo メソッドを呼び出せるようにしています。

[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
public void DisplayCustomerInfo(int CustId)
{
}

さらに詳細な承認が必要であり、データ アクセス メソッド内でロールベースのロジックを実行する必要がある場合には、以下のコードのように、強制的なプリンシパル アクセス許可要求を使用するか、明示のロール確認を使用します。

using System.Security;
using System.Security.Permissions;

public void DisplayCustomerInfo(int CustId)
{
try
  {
// 呼び出し元がマネージャであることを確認するための
// プリンシパル アクセス許可に基づく強制的なロール確認
PrincipalPermission principalPerm = new PrincipalPermission(
null, "Manager");
// 以下のコードは、呼び出し元が "Manager" ロールのメンバである場合
// に限り実行されます。
  }
catch( SecurityException ex )
  {
   . . .
  }
}

次のコードでは、プログラムによる明示のロール確認を使用して、呼び出し元が Manager ロールのメンバであることを確認しています。

public void DisplayCustomerInfo(int CustId)
{
if(!Thread.CurrentPrincipal.IsInRole("Manager"))
  {
    . . .
  }
}

不正なコードを制限する

.NET Framework のコード アクセス セキュリティ (具体的にはコード ID 要求) を使用すると、データ アクセス クラスやデータ アクセス メソッドにアクセスできるアセンブリを制限することができます。

たとえば、自社または特定の開発組織で作成されたコードのみがデータ アクセス コンポーネントを使用できるようにする場合には、次のコードのように、StrongNameIdentityPermission を使用して、呼び出し元アセンブリが厳密名と特定の公開キーを持っていることを確認します。

using System.Security.Permissions;
. . .
[StrongNameIdentityPermission(SecurityAction.LinkDemand, 
PublicKey="002...4c6")]
public void GetCustomerInfo(int CustId)
{
}

特定のアセンブリの公開キーをテキストで表現したものを抽出するには、次のコマンドを使用します。

sn -Tp assembly.dll

注: スイッチ –Tp の "T" は大文字で入力してください。

Web アプリケーション アセンブリは、動的にコンパイルされるため、厳密名を指定することができません。そのため、データ アクセス アセンブリを使用できる Web アプリケーションを限定するのは困難です。最良のアプローチは、カスタム アクセス許可を開発し、そのアクセス許可があるかどうかの確認をデータ アクセス コンポーネントで行うことです。完全に信頼されている Web アプリケーション (または完全に信頼されているコード) はデータ アクセス コンポーネントを呼び出すことができます。しかし、部分的に信頼されているコードがデータ アクセス コンポーネントを呼び出すことができるのは、カスタム アクセス許可を与えられている場合に限られます。

カスタム アクセス許可の実装例については、このガイドの「HOWTO」セクションにある「[HOWTO] 暗号化のカスタム アクセス許可を作成する方法」を参照してください。

データベース内でアプリケーションを制限する

適切なアプローチは、アプリケーションがデータベースへの接続に使用する Windows アカウントに対応する SQL Server ログインを作成するという方法です。作成した SQL Server ログインは、データベース内のデータベース ユーザーにマップする必要があります。その後、そのデータベース ユーザーをユーザー定義のデータベース ロールに追加し、そのロールに権限を与えます。理想的には、アプリケーションが使用するストアド プロシージャに対する実行権限のみをそのロールに与えます。

このアプローチを構成する方法の詳細については、モジュール 19「ASP.NET アプリケーションと Web サービスをセキュリティ保護する」の「ASP.NET アプリケーションのデータ アクセスを構成する」を参照してください。

構成管理

データ アクセス コードでは、データベース接続文字列が構成管理上の主要な考慮事項です。データベース接続文字列の保存場所とセキュリティ保護の方法は、データベース接続文字列に資格情報が含まれる場合には特に慎重に検討する必要があります。暗号管理のセキュリティを高めるには、次のことを行います。

  • Windows 認証を使用します。

  • 接続文字列をセキュリティ保護します。

  • 制限を設定した ACL により UDL ファイルをセキュリティ保護します。

Windows 認証を使用する

Windows 認証を使用すると、資格情報は Windows によって管理されます。また、資格情報がネットワーク経由で転送されなくなります。さらに、接続文字列へのユーザー名とパスワードの埋め込みを回避することができます。

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

SQL 認証を使用する必要がある場合には、接続文字列にユーザー名とパスワードを含めることになります。Web サーバーのソース コードが漏えいする脆弱性が攻撃者に悪用された場合や、攻撃者がサーバーへのログオンに成功した場合には、接続文字列がその攻撃者の手に渡ります。また、そのサーバーへ正当にログインできるユーザーも、同様に接続文字列を見ることができます。接続文字列は、暗号化を使用してセキュリティ保護する必要があります。

接続文字列を暗号化する

接続文字列の暗号化には、DPAPI を使用します。DPAPI による暗号化を使用すると、暗号化キーはプラットフォームによって管理され、特定のコンピュータ アカウントまたは Windows ユーザー アカウントに結び付けられます。そのため、暗号化キーの管理に関する問題を避けることができます。DPAPI を使用するには、P/Invoke を介して Win32 DPAPI 関数を呼び出す必要があります。

マネージ ラッパー クラスを作成する方法の詳細については、http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx にある Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」セクションの「DPAPI ライブラリを作成する方法」を参照してください。

暗号化した接続文字列を安全に保存する

暗号化した接続文字列は、レジストリに保存するか、ファイル (Web.config または Machine.config) に保存することができます。HKEY_LOCAL_MACHINE の下にあるキーを使用する場合は、次のような ACL をそのキーに適用します。

Administrators: フル コントロール
プロセス アカウント: 読み取り

注: プロセス アカウントは、データ アクセス アセンブリが実行されるプロセスに応じて決まります。通常、このプロセスは、ASP.NET プロセスか、Enterprise Services のサーバー プロセス (ソリューションに Enterprise Services 中間層が使用されている場合) です。

代わりに HKEY_CURRENT_USER を使用することを検討してもかまいません。この場合、アクセスを制限することができます。詳細については、モジュール 7「セキュリティ保護されたアセンブリを構築する」の「レジストリ」を参照してください。

注: Microsoft Visual Studio® .NET のデータベース接続用ウィザードを使用すると、接続文字列は、Web アプリケーションの分離コード ファイルか Web.config ファイルのいずれかにプロパティ値としてクリア テキストで保存されます。これらのアプローチは、両方とも避ける必要があります。

制限が設定されているレジストリ キーを使用する場合よりもセキュリティが低下する可能性はありますが、暗号化された文字列を Web.config に保存すると、展開が容易になります。この場合、次に示すように、カスタムの名前と値のペア <appSettings> を使用します。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>  
<add key="connectionString" value="AQA..bIE=" />
</appSettings>
<system.web>
   ...
</system.web>
</configuration>

<appSettings> 要素内の暗号化テキストにアクセスするには、次に示すように、ConfigurationSettings クラスを使用します。

using System.Configuration;
private static string GetConnectionString()
{
return ConfigurationSettings.AppSettings["connectionString"];
}
Persist Security Info='True' (または 'Yes') を使用しない

Persist Security Info 属性を接続文字列に含めると、ユーザーに接続文字列が返される前に ConnectionString プロパティがその接続文字列からパスワードを抜き取るようになります。既定の設定は false であり (これは Persist Security Info 属性を省略した場合と同等です)、データベースへの接続が行われた後、パスワード情報が破棄されます。

制限を設定した ACL により UDL ファイルをセキュリティ保護する

アプリケーションで外部ユニバーサル データ リンク (UDL) ファイルを OLE DB 用の ADO.NET マネージ データ プロバイダと共に使用している場合には、NTFS アクセス許可を使用してアクセスを制限します。次のように制限を設定した ACL を使用してください。

Administrators: フル コントロール
プロセス アカウント: 読み取り

注: UDL ファイルは暗号化されません。よりセキュリティの高いアプローチは、DPAPI を使用して接続文字列を暗号化し、暗号化した文字列を制限が設定されているレジストリ キーに保存するというアプローチです。

機密性の高いデータ

多くの Web アプリケーションは、何らかの形式の機密性の高いデータをデータベースに保存します。攻撃者がデータベースに対するクエリの実行に成功した場合に備えて、クレジット カード番号などの機密性の高いデータ項目を適切に暗号化しておく必要があります。

  • 機密性の高いデータを保存する必要がある場合にはそのデータを暗号化します。

  • 機密性の高いデータをネットワーク上で保護します。

  • salt 処理したパスワード ハッシュを保存します。

機密性の高いデータを保存する必要がある場合にそのデータを暗号化する

機密性の高いデータを保存することは、できるだけ避けてください。機密性の高いデータを保存しなければならない場合は、そのデータを暗号化します。

3DES 暗号化を使用する

クレジット カード番号などの機密性の高いデータをデータベースに保存する場合には、3DES などの強力な対称暗号化アルゴリズムを適用します。

  • 開発時: 3DES 暗号化を使用できるようにするには

  1. RNGCryptoServiceProvider クラスを使用して、強力な (192 ビット、24 バイトの) 暗号化キーを生成します。

  2. 暗号化キーをバックアップして、物理的に安全な場所に保存します。

  3. 暗号化キーを DPAPI で暗号化し、レジストリ キーに保存します。レジストリ キーをセキュリティ保護するため、次のような ACL を適用します。

    Administrators: フル コントロール
    プロセス アカウント (ASPNET など): 読み取り
  • 実行時: データを暗号化してデータベースに保存するには

  1. 暗号化するデータを取得します。

  2. レジストリから暗号化済みの暗号化キーを取得します。

  3. DPAPI を使用して暗号化キーの暗号化を解除します。

  4. TripleDESCryptoServiceProvider クラスを暗号化キーと共に使用して、データを暗号化します。

  5. 暗号化したデータをデータベースに保存します。

  • 実行時: 暗号化された秘密情報の暗号化を解除するには

  1. データベースから暗号化済みのデータを取得します。

  2. レジストリから暗号化済みの暗号化キーを取得します。

  3. DPAPI を使用して暗号化キーの暗号化を解除します。

  4. TripleDESCryptoServiceProvider クラスを使用してデータの暗号化を解除します。

ここで説明した処理手順を採用すると、暗号化キーの暗号化に使用した DPAPI アカウントが損傷した場合には、バックアップを保存した場所から 3DES キーのバックアップを取得して、新しいアカウントで DPAPI を使用してそのバックアップを暗号化することができます。新しく暗号化されたキーはレジストリに保存できます。また、データベースに既に保存されているデータの暗号化解除も引き続き行うことができます。

マネージ DPAPI ライブラリを作成する方法の詳細については、http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx にある Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」セクションの「DPAPI ライブラリを作成する方法」を参照してください。

機密性の高いデータをネットワーク上で保護する

ネットワーク経由でデータベースとの間で送受信される機密性の高いデータには、アプリケーションに固有のデータや、データベースへのログインに必要な資格情報などもあります。ネットワーク上のデータのプライバシーと完全性を確保するには、プラットフォームレベルのソリューション (セキュリティの高いデータ センターが提供する、IPSec で暗号化された通信チャネルをサーバー間で使用するソリューションなど) を使用するか、データベースとの間で SSL 接続を確立するようにアプリケーションを構成します。後者のアプローチを使用するには、データベース サーバーにサーバー証明書がインストールされていることが必要になります。

SSL と IPSec の使用方法の詳細については、http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx にある Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」セクションの「2 つのサーバー間の通信を IPSec で保護する方法」および「SQL Server 2000 との通信を SSL で保護する方法」を参照してください。

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;
}
関連情報

salt 処理されたパスワードを保存するユーザー ストアを実装する方法の詳細については、http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx にある Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」セクションの「SQL Server 2000 でフォーム認証を使用する方法」を参照してください。

例外管理

例外条件は、構成エラー、コードのバグ、または悪意のある入力が原因で発生します。例外管理を適切に行わないと、例外条件が発生したときに、価値の高い接続の詳細に加えて、データ ソースの場所と特徴に関する機密性の高い情報も漏えいする可能性があります。データ アクセス コードに関係する推奨事項は次のとおりです。

  • ADO.NET の例外をトラップしてログに記録します。

  • 必ずデータベース接続を閉じます。

  • ASP.NET アプリケーションで汎用エラー ページを使用します。

ADO.NET の例外をトラップしてログに記録する

データ アクセス コードを try/catch ブロック内に配置して、例外を処理するようにしてください。ADO.NET データ アクセス コードを作成するときに注意が必要なのは、ADO.NET で生成される例外の種類がデータ プロバイダによって異なることです。以下に例を示します。

  • SQL Server 用の .NET Framework データ プロバイダは SqlException を生成します。

  • OLE DB 用の .NET Framework データ プロバイダは OleDbException を生成します。

  • ODBC 用の .NET Framework データ プロバイダは OdbcException を生成します。

例外をトラップする

次のコードは、SQL Server 用の .NET Framework データ プロバイダを使用しているときに種類が SqlException の例外をキャッチするための方法を示しています。

try
{
// データ アクセス コード
}
catch (SqlException sqlex) // 例外の種類を特定しています。
{
}
catch (Exception ex) // 例外の種類を特定していません。
{
}
例外をログに記録する

SqlException クラスから得られる詳細情報をログに記録することも必要です。このクラスは、例外条件の詳細が格納されているプロパティを公開します。これには、エラーの説明を示す Message プロパティ、エラーの種類を一意に識別するための Number プロパティ、追加情報が格納される State プロパティが含まれます。State プロパティは、通常、特定のエラー条件の具体的な発生状況を示すために使用されます。たとえば、あるストアド プロシージャを実行したときに複数の行で同じエラーが発生した場合、State プロパティはこの具体的な発生状況を示します。また、これらのプロパティのほかに Errors コレクションがあります。このコレクションには、SQL Server のエラーについての詳細な情報を提供する SqlError オブジェクトが格納されます。

次のコードは、SQL Server 用の .NET Framework データ プロバイダを使用して SQL Server のエラー条件を処理する方法を示しています。

using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;

// データ アクセス層 (DAL) コンポーネントが公開するメソッド
public string GetProductName( int ProductID )
{
SqlConnection conn = new SqlConnection(
"server=(local);Integrated Security=SSPI;database=products"};
// データ アクセス コード全体を try ブロック内に含めます。
try
  {
conn.Open();
SqlCommand cmd = new SqlCommand("LookupProductName", conn );
cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add("@ProductID", ProductID );
SqlParameter paramPN = 
cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );
paramPN.Direction = ParameterDirection.Output;

cmd.ExecuteNonQuery();
// メソッドが復帰する前に finally ブロックのコードが実行されます。
return paramPN.Value.ToString();  
  }
catch (SqlException sqlex)
  {
// データ アクセスの例外の条件を処理します。
// 例外の詳細をログに記録します。
LogException(sqlex);
// 現在の例外をより適切な外部例外でラップし、
// これにより得られた新しい例外を再スローします。
throw new Exception(
"次の製品 ID に対応する詳細な製品情報の取得に失敗しました:  " + 
ProductID.ToString(), sqlex );
  }
finally
  {
conn.Close(); // 接続を確実に閉じます。
  }
}

// SqlException の詳細をアプリケーション イベント ログに記録するための
// ヘルパー ルーチン
private void LogException( SqlException sqlex )
{
EventLog el = new EventLog();
el.Source = "CustomAppLog";
string strMessage;
strMessage = "例外番号 " + sqlex.Number + 
" の例外 (" + sqlex.Message + ") が発生しました。";
el.WriteEntry( strMessage );

foreach (SqlError sqle in sqlex.Errors)
  {
strMessage = "メッセージ: " + sqle.Message +
" 番号: " + sqle.Number +
" プロシージャ: " + sqle.Procedure +
" サーバー: " + sqle.Server +
" プロバイダ: " + sqle.Source +
" 状態: " + sqle.State +
" 重大度: " + sqle.Class +
" 行番号: " + sqle.LineNumber;
el.WriteEntry( strMessage );
  }
}

必ずデータベース接続を閉じる

例外が発生した場合には、データベース接続を閉じ、その他の限りのあるリソースを解放する必要があります。例外が発生したかどうかに関係なく接続を確実に閉じるには、finally ブロック (C# の場合は using ステートメント) を使用します。上に示したコードでは、finally ブロックが使用されていました。次に示すように、C# の using ステートメントを使用することもできます。

using ((SqlConnection conn = new SqlConnection(connString)))
{
conn.Open();
// 例外が生成された場合、または制御フローが using ステートメントのスコープを正常に
// 離れたときには接続が閉じられます。
}

ASP.NET アプリケーションで汎用エラー ページを使用する

作成するデータ アクセス コードが ASP.NET Web アプリケーションや Web サービスから呼び出されるものである場合には、<customErrors> 要素を構成して、例外の詳細がエンド ユーザーに戻されるのを防止する必要があります。また、この要素を次のように構成すると、汎用エラー ページを指定することができます。

<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />

運用サーバーを使用するときには、mode="On" に設定します。mode="Off" を使用するのは、ソフトウェアをリリースする前の開発およびテストのときだけです。このようにしないと、図 14.4 に示すような大量のエラー情報がエンド ユーザーに戻されます。この情報にはデータベース サーバー名、データベース名、および接続用の資格情報が含まれることがあります。

機密性の高い情報を示す詳細な例外情報

図 14.4
機密性の高い情報を示す詳細な例外情報

図 14.4 は、データ アクセス コードの例外を引き起こした行の近辺に存在するいくつかの脆弱性も示しています。具体的には、次のような脆弱性があります。

  • 接続文字列がハードコードされている。

  • 高度な特権を持つ sa アカウントがデータベースへの接続に使用されている。

  • sa アカウントのパスワードが脆弱である。

  • SQL コマンドの構成が SQL インジェクション攻撃を受けやすいものになっている (入力が検証されておらず、パラメータ化されたストアド プロシージャが使用されていない)。

セキュリティ保護されたデータ アクセス コンポーネントを構築する

次のコードは、CheckProductStockLevel メソッドの実装例を示しています。これは、製品データベースへの在庫数量の問い合わせに使用するメソッドです。このコードは、このモジュールで説明した、データ アクセス コードに求められる重要なセキュリティ機能をいくつか示しています。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using Microsoft.Win32;
using DataProtection;

public static int CheckProductStockLevel(string productCode)
{
int quantity = 0;
// (1) コードを try/catch ブロックで保護しています。
try
  {
// (2) 正規表現を使用して入力検証を行っています。
//     ローカライズを容易にするため、リソース アセンブリからエラー メッセージを取得する
//     ことが推奨されるが、ここではコードを簡潔にするためローカライズ用コードを示しません。
if (Regex.IsMatch(productCode, "^[A-Za-z0-9]{12}$") == false)
throw new ArgumentException("製品コードが無効です。" );
//(3) using ステートメントを使用して、接続が確実に閉じられるようにしています。
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
    {
// (4) パラメータ化されたストアド プロシージャを使用して、
//     SQL インジェクション攻撃に対抗しています。
SqlCommand cmd = new SqlCommand("spCheckProduct", conn);
cmd.CommandType = CommandType.StoredProcedure;

// パラメータの型を確認します。
SqlParameter parm = 
cmd.Parameters.Add("@ProductCode", 
SqlDbType.VarChar,12);
parm.Value = productCode;
// 出力パラメータを定義します。
SqlParameter retparm = cmd.Parameters.Add("@quantity", SqlDbType.Int);
retparm.Direction = ParameterDirection.Output;
conn.Open();
cmd.ExecuteNonQuery();
quantity = (int)retparm.Value;
    }
  }
catch (SqlException sqlex)
  {
// (5) 例外のすべての詳細がログに記録されます。呼び出し元には、SQL エラー
//     コードに基づいて (安全な) 汎用エラー メッセージが返されます。ただし、
//     ここではコードを簡潔にするためログ記録とエラー識別のコードを示しません。
throw new Exception("要求の処理中にエラーが発生しました。");
  }
catch (Exception ex)
  {
// 例外のすべての詳細をログに記録します (コードは省略)。
throw new Exception("要求の処理中にエラーが発生しました。");
  }
return quantity;
}

// (6) 暗号化されたデータベース接続文字列がレジストリに保存されています。
private static string GetConnectionString()
{
// レジストリから暗号化テキストを取得する。このテキストを含むレジストリ キー
// の ACL では、プロセス アカウントに読み取りアクセス許可を与えておく必要があります。
string encryptedString = (string)Registry.LocalMachine.OpenSubKey(
@"Software\OrderProcessing\")
.GetValue("ConnectionString");
// マネージ DPAPI ヘルパー ライブラリを使用して接続文字列の暗号化を解除します。
DataProtector dp = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
byte[] dataToDecrypt = Convert.FromBase64String(encryptedString);
return Encoding.ASCII.GetString(dp.Decrypt(dataToDecrypt,null));
}

ここで紹介したコードは、次のようなセキュリティ上の特徴を持っています (箇条書きの番号は、コードのコメント行にある番号に対応しています)。

  1. データ アクセス コードが try/catch ブロック内に配置されている: これは、例外が発生した場合にシステム レベルの情報が呼び出し元に返されるのを防止するために必要です。呼び出し元の ASP.NET Web アプリケーションや Web サービスで例外を処理し、適切な汎用エラー メッセージをクライアントに戻すこともできますが、ここで示したデータ アクセス コードではこのような処理を利用していません。

  2. 正規表現を使用して入力が検証されている: 与えられた製品 ID について、A ~ Z、a ~ z、または 0 ~ 9 の範囲の文字のみを含んでいるか、および 12 文字を超えていないかを確認しています。これは、SQL インジェクション攻撃を防止するための一連の対策のうちの第一のものです。

  3. Microsoft Visual C#® の using ステートメントの内部で SqlConnection オブジェクトが作成されている: これにより、例外が発生したかどうかに関係なく、メソッドの内部で接続が確実に閉じられます。その結果、使用可能なデータベース接続をすべて使用することを試みるサービス拒否 (DoS) 攻撃の脅威が軽減されます。finally ブロックを使用してこれと同様の機能を実現することもできます。

  4. パラメータ化されたストアド プロシージャがデータ アクセスに使用されている: これは、SQL インジェクション攻撃を防止するためのもう 1 つの対策です。

  5. 詳細なエラー情報がクライアントに返されない: 例外の詳細は、問題の診断に役立つように、ログに記録されます。

  6. 暗号化されたデータベース接続文字列がレジストリに保存されている: データベース接続文字列の保存方法のうち最もセキュリティが高いのは、DPAPI を使用して接続文字列を暗号化し、ACL でセキュリティ保護したレジストリ キーに暗号化テキストを保存する方法です。たとえば、Administrators にはフル コントロールを許可し、ASP.NET のプロセス アカウントまたは Enterprise Services のプロセス アカウント (どちらになるかはコンポーネントをホストするプロセスに応じて決まります) には読み取りを許可します。

    注: ここで紹介したコードは、レジストリから接続文字列を取得し、マネージ DPAPI ヘルパー ライブラリを使用してその文字列の暗号化を解除する方法を示しています。このライブラリは、 Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「作業の手引き」セクションの「DPAPI ライブラリを作成する方法」で紹介されています。

コード アクセス セキュリティに関する考慮事項

データ アクセスは、いずれも、コード アクセス セキュリティのアクセス許可要求の対象になります。要件の詳細は、どの ADO.NET マネージ データ プロバイダを使用するかによって異なります。次の表に、データ アクセス アセンブリに与える必要があるアクセス許可を、ADO.NET データ プロバイダごとに示します。

表 14.1: 必要なコード アクセス セキュリティ アクセス許可 (ADO.NET データ プロバイダ別)

ADO.NET データ プロバイダ

必要なコード アクセス セキュリティ アクセス許可

SQL Server

SqlClientPermission
中程度の信頼を与えられている Web アプリケーションを含む、部分的に信頼されている呼び出し元をサポートします。

OLE DB

OleDbPermission*

Oracle

OraclePermission*

ODBC

OdbcPermission*

* この資料の執筆時点では、OLE DB 用、Oracle 用、および ODBC 用のデータ プロバイダが .NET Framework 1.0 と 1.1 でサポートする呼び出し元は、完全に信頼されている呼び出し元のみとなっています。これらのプロバイダを部分的に信頼されている Web アプリケーションから使用するには、データ アクセス コードをサンドボックス内で実行する必要があります。これには、専用のデータ アクセス アセンブリが必要です。モジュール 9「ASP.NET でコード アクセス セキュリティを使用する」では、データ アクセス コードをサンドボックス内で実行し、信頼が中程度の Web アプリケーションから OLE DB データ プロバイダを使用するには、どのようにすればよいかを例示しています。

SQL Server 用の ADO.NET データ プロバイダを使用する場合には、コード アクセス セキュリティ ポリシーを通じて SqlClientPermission がコードに与えられていなければなりません。完全な信頼または中程度の信頼が与えられている Web アプリケーションは、このアクセス許可を持っています。

コードに SqlClientPermission が与えられているかどうかによって、そのコードが SQL Server に接続できるかどうかが決まります。また、このアクセス許可を使用してデータベース接続文字列の使用に制限を設けることもできます。たとえば、アプリケーションに対して統合セキュリティを使用するように強制することや、SQL Server のセキュリティを使用する場合は空のパスワードを許容しないようにすることができます。SqlClientPermission により指定した規則に対する違反があった場合には、実行時セキュリティ例外が発生します。

SqlClientPermission を使用してデータ アクセスを制限する方法の詳細については、モジュール 8「コード アクセス セキュリティの実践」の「データ アクセス」を参照してください。

展開に関する考慮事項

データ アクセス コンポーネントは、安全に設計され開発されていても、安全な方法で展開しなければ攻撃に対して脆弱なままになります。一般的な展開方法は、データ アクセス コードとデータベースを別々のサーバーに展開するという方法です。多くの場合、これらのサーバーの間は内部ファイアウォールで隔てられています。これにより、展開に関する新たな考慮事項が生じています。開発者と管理者は、次の事項に注意する必要があります。

  • ファイアウォールに関する制限事項

  • 接続文字列の管理

  • ログイン アカウントの構成

  • ログオンの監査

  • ネットワーク上でのデータのプライバシーと完全性

ファイアウォールに関する制限事項

ファイアウォール経由で SQL Server に接続する場合には、ファイアウォール、クライアント、およびサーバーで構成作業が必要になります。クライアントの構成には、SQL Server のクライアント ネットワーク ユーティリティを使用します。データベース サーバーの構成には、サーバー ネットワーク ユーティリティを使用します。SQL Server は既定では TCP ポート 1433 をリッスンしますが、これは変更できます。SQL Server でリッスンするポートに指定したポートと同じポートをファイアウォールで開く必要があります。

SQL Server の認証モードによっては、また、アプリケーションでの分散トランザクションの使われ方によっては、ファイアウォール上のポートをいくつか追加で開く必要が生じることがあります。

  • アプリケーションから SQL Server への接続に Windows 認証を使用する場合には、Kerberos 認証または NTLM 認証をサポートするのに必要なポートを開く必要があります。

    Active Directory を使用していないネットワークの場合、通常、Windows 認証には TCP ポート 139 が必要です。ポートに関する要件については、TechNet の http://www.microsoft.com/resources/documentation/windows/2000/server/reskit/en-us/tcpip/part4/tcpappc.mspx にある記事「TCP and UDP Port Assignments」(英語) および http://www.microsoft.com/japan/technet/security/bestprac/bpent/sec2/seconaa.mspx にある記事「管理権限のセキュリティに関する考慮事項」を参照してください。

  • また、アプリケーションで分散トランザクション (自動 COM+ トランザクションなど) を使用している場合には、個々の DTC インスタンスの間、および DTC とリソース マネージャ (SQL Server など) の間での DTC トラフィックを許可するように、ファイアウォールを構成する必要が生じることがあります。

構成の詳細については、モジュール 18「データベース サーバーをセキュリティ保護する」の「ポート」を参照してください。

接続文字列の管理

多くのアプリケーションでは、主にパフォーマンス上の理由から、接続文字列をコード内に保存しています。しかし、これにより得られるパフォーマンス上の利点はごくわずかです。また、ファイル システム キャッシュを使用すれば、接続文字列を外部ファイルに保存しても、同等のパフォーマンスが得られます。接続文字列の保存に外部ファイルを使用する方法は、システム管理上優れた方法です。

セキュリティの強化という観点から推奨されるアプローチは、DPAPI を使用して接続文字列を暗号化するアプローチです。接続文字列にユーザー名とパスワードが使用されている場合には、このアプローチが特に重要になります。また、暗号化された接続文字列の保存先を決定する必要があります。レジストリに保存する場合、HKEY_CURRENT_USER が特にセキュリティの高い場所です。この場所にアクセスできるのは、この場所に関連付けられているユーザー アカウントで実行されたプロセスに限られるからです。この手法のほかに、暗号化された接続文字列を Web.config ファイルに保存するという、展開が容易になる手法もあります。どちらの手法も、このモジュールの「構成管理」で説明しています。

ログイン アカウントの構成

アプリケーションがデータベースへの接続に使用するアカウントは、最小権限のアカウントにする必要があります。これは、SQL インジェクション攻撃の脅威を軽減するための主なテクニックの 1 つです。

開発者は、アプリケーションがログインしてアクセスする必要があるストアド プロシージャは具体的にどれであるかを (場合によってはテーブルについても) データベースの管理者に連絡しなければなりません。理想的には、アプリケーションのログインには、そのアプリケーションと共に展開された、限られた範囲のストアド プロシージャに対する実行権限のみを与えるようにします。

アプリケーションがデータベースへの接続に使用する SQL アカウントまたは Windows アカウントには、強力なパスワードを使用してください。

データベース上のアプリケーション アカウントに関して承認プロセスから見て推奨される戦略については、このモジュールの「承認」を参照してください。

ログオンの監査

SQL Server は、失敗したログインを (できれば成功したログインと共に) ログに記録するように構成する必要があります。失敗したログインを監査することは、アカウントのパスワードを検出しようとしている攻撃者を発見するのに役立ちます。

SQL Server の監査を構成する方法の詳細については、モジュール 18「データベース サーバーをセキュリティ保護する」を参照してください。

ネットワーク上でのデータのプライバシーと完全性

SQL Server への接続に SQL 認証を使用する場合は、ログインに必要な資格情報がネットワーク上で漏えいしないようにする必要があります。そのためには、データベース サーバーに証明書をインストールするか (これにより資格情報が暗号化されるようになります)、IPSec で暗号化したチャネルを使用してデータベースに接続します。

機密性の高いアプリケーション レベルのデータをデータベースとの間で送受信する場合には、IPSec または SSL を使用してデータベースに接続することをお勧めします。詳細については、モジュール 18「データベース サーバーをセキュリティ保護する」を参照してください。

要約

このモジュールでは、データ アクセス コードに対する主な脅威を示し、一般的な脆弱性について説明しました。SQL インジェクションは注意を要する主要な脅威の 1 つです。このモジュールで説明した適切な対策を実施しなければ、攻撃者がデータ アクセス コードを悪用してデータベースで任意のコマンドを実行する可能性があります。ファイアウォールや SSL などの従来のセキュリティ対策は、SQL インジェクション攻撃に対する防御にはなりません。この種類の攻撃への防御としては、少なくとも、入力を十分に検証することとパラメータ化されたストアド プロシージャを使用する必要があります。

その他のリソース

さらに詳しい情報については、次のリソースを参照してください。

  • 印刷可能なチェックリストについては、このガイドの「チェックリスト」にある「チェックリスト: データ アクセスをセキュリティ保護する」を参照してください。

  • 開発者のワークステーションをセキュリティ保護する方法については、「HOWTO」にある「[HOWTO] 開発者のワークステーションのセキュリティを確保する方法」を参照してください。

  • SQL Server で SSL を使用する方法については、http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx にある Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」セクションの「SQL Server 2000 との通信を SSL で保護する方法」を参照してください。

  • IPSec を使用する方法については、http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx にある Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」セクションの「2 つのサーバー間の通信を IPSec で保護する方法」を参照してください。

  • DPAPI を使用する方法については、http://www.microsoft.com/japan/msdn/net/security/SecNetHT00.aspx にある Microsoft patterns & practices「セキュリティ保護された ASP.NET アプリケーションの構築: 認証、認定、および通信のセキュリティ保護」の「索引」セクションの「DPAPI ライブラリを作成する方法」を参照してください。


この情報は役に立ちましたか。
(残り 1500 文字)