コード アクセス セキュリティと ADO.NET
更新 : November 2007
.NET Framework はコード アクセス セキュリティ (CAS) に加えてロール ベースのセキュリティを備えています。どちらも、共通言語ランタイム (CLR) が提供する共通のインフラストラクチャを使って実装されています。アンマネージ コードの場合、ほとんどのアプリケーションはユーザーまたはプリンシパルの権限で実行されます。そのため、悪意のあるソフトウェアやエラーを含むソフトウェアが、システム特権を持つユーザーによって実行された場合、コンピュータ システムが被害を受けたり、機密データが改ざんされる可能性があります。
これに対し、.NET Framework で実行されるマネージ コードには、コードそのものに適用されるコード アクセス セキュリティが存在します。コードの実行が許可されるかどうかは、プリンシパルの ID だけでなく、コードの作成元など、コードの ID を表す情報に依存します。これにより、マネージ コードが誤用される可能性が低くなります。
コードのアクセス権限
コードは、実行されるときに、CLR のセキュリティ システムで評価される証拠を提示します。通常、この証拠は、URL、サイト、ゾーン、アセンブリの ID を保証するデジタル署名などのコードの作成元に関する情報で構成されます。
CLR は、そのコードに許されている操作の範囲内で実行を許可します。コードは権限を要求できますが、その要求は管理者が設定したセキュリティ ポリシーに基づいて受理されます。
![]() |
---|
CLR で実行されるコード自体に、アクセス許可を与えることはできません。たとえば、コードから要求できる権限は、セキュリティ ポリシーによって許可されたレベルよりも低い権限だけであり、それを超える権限が付与されることはありません。権限を付与する場合は、まず、権限をまったく付与しない状態から始め、その後で、実行しようとする特定のタスクに必要な最小限の権限を追加してゆくようにします。すべての権限を与えてから、不要なものを 1 つずつ拒否していく方法では、アプリケーションを危険にさらす結果となります。必要以上の権限を付与することによって意図しないセキュリティ ホールを招く可能性があります。詳細については、「セキュリティ ポリシーの設定」および「セキュリティ ポリシーの管理」を参照してください。 |
コードのアクセス権限には、次の 3 種類があります。
Code access permissions。CodeAccessPermission クラスから派生します。ファイルや環境変数などの保護されたリソースにアクセスして、保護された操作 (アンマネージ コードへのアクセスなど) を実行するには権限が必要です。
Identity permissions。アセンブリを識別する特性を表します。アセンブリには証拠に基づいて権限が付与されます。証拠には、デジタル署名やコードの作成元などの情報が含まれます。ID 権限も CodeAccessPermission 基本クラスから派生します。
Role-based security permissions。プリンシパルが、指定された ID を持っているか、または、指定されたロールに属しているかどうかに基づきます。PrincipalPermission クラスにより、アクティブなプリンシパルに対する宣言型および命令型の権限チェックが可能となります。
ランタイムのセキュリティ システムは、特定のリソースへのアクセスまたは特定の操作の実行がコードに許されているかどうかを判断するため、呼び出し履歴をたどりながら、各呼び出し元に付与されている権限と、要求されている権限とを比較します。呼び出し履歴に、要求された権限を持たない呼び出し元が 1 つでも見つかった場合、SecurityException がスローされてアクセスが拒否されます。
権限の要求
権限を要求する目的は、そのアプリケーションを実行するために必要な権限をランタイムに伝えると共に、実際に必要な権限以外は付与されないようにすることです。たとえば、ローカル ディスクにデータを書き込むアプリケーションは FileIOPermission を必要とします。この権限が付与されていない場合、アプリケーションがディスクへの書き込みを試行した時点で実行に失敗します。ただし、アプリケーションから FileIOPermission を要求した場合、その権限が付与されなければ、最初の段階で例外が生成され、アプリケーションが読み込まれることはありません。
ディスクからのデータの読み取りだけを必要とするアプリケーションでは、書き込み権限が決して付与されないように要求できます。バグが存在していたり、悪意のある攻撃を受けたとしても、操作の対象となるデータに損害を与えることはありません。詳細については、「アクセス許可の要求」を参照してください。
ロール ベースのセキュリティと CAS
ロール ベースのセキュリティとコード アクセス セキュリティ (CAS) の両方を実装することで、アプリケーションの全体的なセキュリティを高めることができます。ロール ベースのセキュリティには、Windows アカウントまたはカスタム ID を使用できます。セキュリティ プリンシパルに関する情報には、現在のスレッドからアクセスできます。また、ユーザーによって指定された資格情報に基づいて、データやリソースへのアクセスを提供するアプリケーションも少なくありません。このようなアプリケーションは、通常、ユーザーのロールを調べ、そのロールに基づいてリソースへのアクセスを許可します。
ロール ベースのセキュリティを使用することで、コンポーネントが、現在のユーザーおよびそのユーザーに関連付けられているロールを実行時に特定できるようになります。この情報を CAS ポリシーを使ってマッピングすることによって、一連の権限が実行時に判別されます。特定のアプリケーション ドメインでは、ホストが既定のロール ベース セキュリティ ポリシーを変更し、ユーザーおよびそのユーザーに関連付けられたロールを表す既定のセキュリティ プリンシパルを設定できます。
CLR にはマネージ コードに制限を適用するためのメカニズムが、権限を使用することによって実装されています。ロール ベースのセキュリティ権限により、ユーザー (またはユーザーのために行動するエージェント) が特定の ID を持っているかどうか、または特定のロールに所属しているかどうかを検出するメカニズムが実現されます。詳細については、「アクセス許可」を参照してください。
開発しているアプリケーションの種類によっては、データベースに対するロール ベースの権限の実装も考慮する必要があります。SQL Server におけるロール ベースのセキュリティの詳細については「SQL Server のセキュリティ (ADO.NET)」を参照してください。
アセンブリ
アセンブリは、.NET Framework アプリケーションの配置、バージョン管理、再利用、アクティベーション スコープ、およびセキュリティ権限の基本単位です。アセンブリは、互いに連携して動作するように作成された一連の型やリソースを提供し、"機能" の論理上の単位を成すものです。CLR から見て、型がアセンブリのコンテキスト外に存在することはありません。アセンブリの作成と配置の詳細については、「アセンブリを使用したプログラミング」を参照してください。
厳密な名前付きアセンブリ
厳密な名前 (デジタル署名) は、アセンブリの ID (単純なテキスト名、バージョン番号、および使用可能な場合はカルチャ情報) と、公開キーおよびデジタル署名で構成されます。このデジタル署名は、対応する秘密キーを使用してアセンブリ ファイルから生成されます。アセンブリ ファイルにはアセンブリ マニフェストが格納されており、そこに、アセンブリを構成するすべてのファイルの名前とハッシュが含まれます。
アセンブリに厳密な名前を付けることで、アプリケーションやコンポーネントに一意の ID が与えられます。他のソフトウェアは、その ID を使用することで、対応するアプリケーションまたはコンポーネントを明示的に参照できます。アセンブリに厳密な名前を付けると、悪意のあるコードを含むアセンブリのなりすましから保護することができます。また、異なるバージョンのコンポーネント間でバージョンの整合性をとることもできます。グローバル アセンブリ キャッシュ (GAC) に展開されるアセンブリには、厳密な名前を付ける必要があります。詳細については、「厳密な名前付きアセンブリの作成と使用」を参照してください。
ADO.NET 2.0 での Partial Trust (部分信頼)
ADO.NET 2.0 では、NET Framework Data Provider for SQL Server、.NET Framework Data Provider for OLE DB、NET Framework Data Provider for ODBC、および .NET Framework Data Provider for Oracle のすべてが、部分的に信頼された環境でも実行できるようになりました。.NET Framework の以前のリリースでは、信頼性のレベルが Full trust よりも低いアプリケーションでは System.Data.SqlClient のみがサポートされていました。
SQL Server プロバイダを使用する部分的に信頼できるアプリケーションは、少なくとも、Execution アクセス許可と SqlClientPermission アクセス許可が必要です。
Partial Trust (部分信頼) のアクセス許可属性プロパティ
部分信頼のシナリオでは、SqlClientPermissionAttribute メンバを使用して、.NET Framework Data Provider for SQL Server が使用できる機能をさらに制限することができます。
![]() |
---|
.NET Framework Data Provider for SQL Server では、SQL Server 2000 の SQL デバッグを有効にして SqlConnection を開くために Full Trust (完全信頼) セキュリティ権限が必要です。ただし、SQL Server 2005 の SQL デバッグでは、このクラスは使用しません。詳細については、SQL Server 2005 オンライン ブックを参照してください。 |
使用可能な SqlClientPermissionAttribute プロパティとその説明を次の表に示します。
アクセス許可属性プロパティ |
説明 |
---|---|
Action |
セキュリティ アクションを取得または設定します。このプロパティは、SecurityAttribute から継承されています。 |
AllowBlankPassword |
接続文字列内で空白のパスワードの使用を許可または禁止します。有効な値は、空白のパスワードの使用を許可する true および空白のパスワードの使用を禁止する false です。このプロパティは、DBDataPermissionAttribute から継承されています。 |
ConnectionString |
使用できる接続文字列を指定します。複数の接続文字列を指定できます。 ![]()
接続文字列には、ユーザー ID やパスワードを含めないでください。このリリースでは、.NET Framework 構成ツールを使用して接続文字列制限を変更することはできません。
このプロパティは、DBDataPermissionAttribute から継承されています。 |
KeyRestrictions |
許可または禁止する接続文字列パラメータを指定します。接続文字列パラメータは、<parameter name>= の形式で指定します。セミコロン (;) で区切って、複数のパラメータを指定できます。 ![]()
KeyRestrictions が指定されておらず、KeyRestrictionBehavior プロパティが AllowOnly または PreventUsage に設定されている場合は、接続文字列パラメータを追加できません。このプロパティは、DBDataPermissionAttribute から継承されています。
|
KeyRestrictionBehavior |
接続文字列パラメータが、唯一追加を許可される接続文字列パラメータ (AllowOnly) か、または追加を禁止される接続文字列パラメータ (PreventUsage かを指定します。既定値は AllowOnly です。このプロパティは、DBDataPermissionAttribute から継承されています。 |
TypeID |
派生クラスで実装すると、この属性の一意の識別子を取得します。このプロパティは、Attribute から継承されています。 |
Unrestricted |
このリソースに対する無制限のアクセス許可が宣言されているかどうかを示します。このプロパティは、SecurityAttribute から継承されています。 |
ConnectionString の構文
次の例では、特定の接続文字列のみ使用できるようにするために、構成ファイルの connectionStrings 要素を使用する方法を示しています。構成ファイルからの接続文字列の格納および取得の詳細については、「接続文字列 (ADO.NET)」を参照してください。
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;" />
</connectionStrings>
KeyRestrictions の構文
上述の接続文字列の使用を許可し、Encrypt、Packet、Size 接続文字列オプションの使用も許可する一方で、その他の接続文字列オプションの使用を禁止する例を次に示します。
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>
KeyRestrictionBehavior を PreventUsage に設定する場合の構文
上述の接続文字列の使用を許可し、User Id、Password、および Persist Security Info を除く他のすべての接続パラメータを許可する例を次に示します。
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="User Id=;Password=;Persist Security Info=;"
KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>
KeyRestrictionBehavior を AllowOnly に設定する場合の構文
Initial Catalog、Connection Timeout、Encrypt、および Packet Size パラメータも含まれる 2 つの接続文字列の使用を許可する例を次に示します。その他の接続文字列パラメータは、すべて制限されます。
<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=true;"
KeyRestrictions="Initial Catalog;Connection Timeout=;
Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
<add name="DatabaseConnection2"
connectionString="Data Source=SqlServer2;Initial
Catalog=Northwind2;Integrated Security=true;"
KeyRestrictions="Initial Catalog;Connection Timeout=;
Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>
カスタム アクセス許可セットを使用した Partial Trust の有効化
特定のゾーンに対して System.Data.SqlClient アクセス許可を有効にするには、システム管理者がカスタム アクセス許可セットを作成して、目的のゾーンに指定する必要があります。LocalIntranet などの既定のアクセス許可セットは変更できません。たとえば、Zone が LocalIntranet であるコードに System.Data.SqlClient アクセス許可をインクルードするには、システム管理者は LocalIntranet アクセス許可セットをコピーして名前を "CustomLocalIntranet" に変更し、System.Data.SqlClient アクセス許可を追加します。次に、コード アクセス セキュリティ ポリシー ツール (Caspol.exe) を使用して CustomLocalIntranet アクセス許可セットをインポートし、LocalIntranet_Zone のアクセス許可セットを CustomLocalIntranet に設定します。
サンプル アクセス許可セット
部分信頼のシナリオでの、.NET Framework Data Provider for SQL Server 用アクセス許可セットの例を次に示します。カスタム アクセス許可セットを作成する方法の詳細については、「Caspol.exe によるアクセス許可セットの構成」を参照してください。
<PermissionSet class="System.Security.NamedPermissionSet"
version="1"
Name="CustomLocalIntranet"
Description="Custom permission set given to applications on
the local intranet">
<IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Integrated Security=true;"
KeyRestrictions="Initial Catalog=;Connection Timeout=;
Encrypt=;Packet Size=;"
KeyRestrictionBehavior="AllowOnly" />
</IPermission>
</PermissionSet>
セキュリティ アクセス許可を使用した ADO.NET コード アクセスの検証
部分信頼のシナリオでは、SqlClientPermissionAttribute を指定することによって、コード内の特定のメソッドに対する CAS 特権を要求できます。制限されたセキュリティ ポリシーによって、実際にはその特権が許可されていない場合は、そのコードを実行する前に例外がスローされます。セキュリティ ポリシーの詳細については、「セキュリティ ポリシーの管理」および「セキュリティ ポリシーの実施」を参照してください。
例
次のサンプルは、特定の接続文字列を必要とするコードの作成方法を示しています。このサンプルは、System.Data.SqlClient に対する無制限の権限を拒否する機能をシミュレートします。この機能は、実際には、システム管理者が、CAS ポリシーを使用して実装します。
![]() |
---|
ADO.NET の CAS 権限を定義する場合、最も制限の厳しいケース (権限なし) から開始し、その後で、コードが実行する特定のタスクに必要な特定の権限を追加するというのが、適切なパターンです。逆に、すべての権限を与えてから特定の権限を拒否してゆく方法は、安全ではありません。同じ接続文字列を表現する方法が多数あるためです。たとえば、すべての権限を与えた後で接続文字列 "server=someserver" の使用を拒否しても、"server=someserver.mycompany.com" という接続文字列は使用可能です。常に権限をまったく与えない状態から開始することで、権限セットのセキュリティ ホールを減らすことができます。 |
SqlClient でセキュリティ確認要求を実行する方法を、次のコードに示します。CAS の権限が適切に設定されていない場合、SecurityException がスローされます。SecurityException の出力は、コンソール ウィンドウに表示されます。
Private Sub TestCAS(ByVal connectString1 As String, ByVal connectString2 As String)
' Simulate removing SqlClient permissions.
Dim permission As New SqlClientPermission(PermissionState.Unrestricted)
permission.Deny()
' Try to open a connection.
Try
Using connection As New SqlConnection(connectString1)
connection.Open()
Console.WriteLine("Connection opened, unexpected.")
End Using
Catch ex As System.Security.SecurityException
Console.WriteLine("Failed, as expected: {0}", _
ex.FirstPermissionThatFailed)
' Uncomment the following line to see Exception details.
' Console.WriteLine("BaseException: {0}", ex.GetBaseException())
End Try
SqlClientPermission.RevertAll()
' Add permission for a specific connection string.
' This would typically be achieved by the administrator
' deploying a CAS policy, not in your code.
permission = New SqlClientPermission(PermissionState.None)
permission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly)
permission.PermitOnly()
' Try again, it should succeed now.
Try
Using connection As New SqlConnection(connectString1)
connection.Open()
Console.WriteLine("Connection opened, as expected.")
End Using
Catch ex As System.Security.SecurityException
Console.WriteLine("Unexpected failure: {0}", ex.Message)
End Try
' Try a different connection string. This should fail.
Try
Using connection As New SqlConnection(connectString2)
connection.Open()
Console.WriteLine("Connection opened, unexpected.")
End Using
Catch ex As System.Security.SecurityException
Console.WriteLine("Failed, as expected: {0}", ex.Message)
End Try
End Sub
static void TestCAS(string connectString1, string connectString2)
{
// Simulate removing SqlClient permissions.
SqlClientPermission permission = new
SqlClientPermission(PermissionState.Unrestricted);
permission.Deny();
// Try to open a connection.
try
{
using (SqlConnection connection = new
SqlConnection(connectString1))
{
connection.Open();
Console.WriteLine("Connection opened, unexpected.");
}
}
catch (System.Security.SecurityException ex)
{
Console.WriteLine("Failed, as expected: {0}",
ex.FirstPermissionThatFailed);
// Uncomment the following line to see Exception details.
//Console.WriteLine("BaseException: " + ex.GetBaseException());
}
SqlClientPermission.RevertAll();
// Add permission for a specific connection string.
// This would typically be achieved by the administrator deploying
// a CAS policy, not in your code.
permission = new SqlClientPermission(PermissionState.None);
permission.Add(connectString1,"", KeyRestrictionBehavior.AllowOnly);
permission.PermitOnly();
// Try again, it should succeed now.
try
{
using (SqlConnection connection = new SqlConnection(connectString1))
{
connection.Open();
}
Console.WriteLine("Connection opened, as expected.");
}
catch (System.Security.SecurityException ex)
{
Console.WriteLine("Unexpected failure: {0}", ex.Message);
}
// Try a different connection string. This should fail.
try
{
using (SqlConnection connection = new SqlConnection(connectString2))
{
connection.Open();
}
Console.WriteLine("Connection opened, unexpected.");
}
catch (System.Security.SecurityException ex)
{
Console.WriteLine("Failed, as expected: {0}", ex.Message);
}
}
コンソール ウィンドウに表示される出力の例を次に示します。
Failed, as expected: <IPermission class="System.Data.SqlClient.
SqlClientPermission, System.Data, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1"
AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Initial Catalog=
Northwind;Integrated Security=SSPI" KeyRestrictions=""
KeyRestrictionBehavior="AllowOnly"/>
</IPermission>
Connection opened, as expected.
Failed, as expected: Request failed.
アンマネージ コードとの相互運用性
CLR の外部で実行されるコードはアンマネージ コードと呼ばれます。したがって、CAS などのセキュリティ メカニズムをアンマネージ コードに適用することはできません。アンマネージ コードの例としては、COM コンポーネント、ActiveX インターフェイス、Win32 API 関数があります。アンマネージ コードを実行する場合は、アプリケーションの全体的なセキュリティが損なわれないよう特別な考慮をする必要があります。詳細については、「アンマネージ コードとの相互運用」を参照してください。
.NET Framework は、COM 相互運用機能を介したアクセスを提供することによって、既存の COM コンポーネントとの下位互換性をサポートしています。COM 相互運用ツールを使って適切な COM 型をインポートすることにより、.NET Framework アプリケーションに COM コンポーネントを組み込むことができます。インポートが完了すると、COM 型を使用できるようになります。アセンブリのメタデータをタイプ ライブラリにエクスポートし、マネージ コンポーネントを COM コンポーネントとして登録することで、COM クライアントからマネージ コードにアクセスすることもできます。詳細については、「高度な COM 相互運用性」を参照してください。