印刷用ページ       送信     
クリックして評価とフィードバックをお寄せください
TechNet
TechNet ライブラリ
テクニカルドキュメント
SQL Server
SQL Server 2005
データベースエンジン
 SQL Server 2005 を使用して、行レベル セキュリティとセ...

  低帯域幅での表示をオンにする
SQL Server 2005 を使用して、行レベル セキュリティとセル レベル セキュリティを分類されたデータベースに実装する
このページはアーカイブです。記載されている内容は情報提供のみを目的としており、ページ内のリンクは有効でない可能性がありますが、これらの情報についてマイクロソフトはいかなる責任も負わないものとします。
公開日: 2005年9月29日

Art Rask、Don Rubin、Bill Neumann (Microsoft Consulting Services)

概要   このホワイトペーパーでは、SQL Server 2005 を使用して行レベル セキュリティ (RLS : Row Level Security) とセル レベル セキュリティ (CLS : Cell Level Security) をサポートする方法について説明します。また、分類されたデータベースのセキュリティ要件を RLS および CLS を使用してどのように満たすかを、例を挙げて説明します。

トピック

概要
前提条件と設計原則
きめ細かなアクセス制御
セキュリティ ラベルの概要
データベース ソリューションの概要
行レベル セキュリティ
各設計の統合 (第 1 部)
セル レベル セキュリティ
物理的な分割
各設計の統合 (第 2 部)
パフォーマンス
行レベルの漏洩
まとめ
付録 1 : instead-of トリガの例
付録 2 : 対称キーの暗号化オプション

概要

このホワイトペーパーでは、セキュリティ ラベルに基づいて、複数レベルのデータベース セキュリティの要件について説明します。Microsoft® SQL Server™ 2005 をベースとした設計を紹介します。この設計では、セキュリティ ラベルとビューに基づいて行レベル セキュリティ (RLS) を設定しています。セル レベル セキュリティ (CLS) を設定する追加の設計要素も組み込みました。CLS もセキュリティ ラベルに基づいて制御されます。

前提条件と設計原則

ここで説明するアプローチには、データベースを使用するアプリケーションでは個々のエンド ユーザーの ID を使用して接続する、というアーキテクチャ上の前提条件があります。このとき使用されるのは、Windows 統合認証または SQL Server ログインです。例外として、ID が 1 つの中間層の接続プールを使用する場合があります。このような接続プールは、一般に、スケーラビリティと管理上の利点から使用されます。ただし、行レベル セキュリティのラベル付けを必要とする重要なセキュリティ要件のあるシステムでは、通常、データベース接続が終了するまでユーザー ID を保持するという監査要件があります。そのため、通常は、接続プールの使用は選択肢となり得ません。

また、この設計は以下の原則に従っています。

  1. SQL Server と Microsoft Windows® のセキュリティ モデルを使用すること。このセキュリティ モデルから外れる認証メカニズムは避けます。

  2. 簡潔さを保つこと。一般的な状況では、セキュリティ ラベルの組み合わせ数は膨れ上がる傾向があります。細かいセキュリティ グループを増やすのは避けます。そうしなくてもデータをきめ細かく制御することは可能です。

  3. 数千万行単位の大規模なデータベースまで対応できる設計にすること。

きめ細かなアクセス制御

情報のアクセス制御は、多くのコンピュータ ソフトウェアの基本である、ユーザーのアクセス許可に基づいて行っていました。たとえば、Microsoft Windows では、アクセス制御リスト (ACL : Access Control List) を使用して、NTFS ファイル システムのファイルやフォルダにアクセスできるユーザーを制御します。Microsoft SQL Server では、サーバー ログイン、データベース、データベース内のオブジェクト (テーブルなど) に対するアクセス制御を強制適用しています。いずれの場合も、情報の制御レベルは、ある程度の細かさまでしか設定できません。Windows ではユーザー ファイルへのアクセスを制御できますが、ユーザー ファイル内の一部の情報へのアクセスを制御することはできません。SQL Server 2000 は、他の多くの RDBMS と同様に、テーブルへのアクセスを制御しますが、テーブル内の行レベルまたはセル レベルのセキュリティ機能はありません。

けれども場合によっては、より細かいレベルでアクセスを制御する必要があります。たとえば、複数の患者とその診断情報が 1 つのファイルやテーブルに格納されているとします。一方で、個々の医師には、自分の担当患者に関する情報を確認する権限しか与えない場合があります。このような場合、単に 1 ファイルに ACL を設定したり、テーブルで GRANT/DENY SELECT を発行したりするだけでは、このニーズを満たすことはできません。

同様の状況が他にも多数存在します。たとえば、金融、法律、政府、軍事などに向けたアプリケーションです。さらに、消費者のプライバシー要件により、データをよりきめ細かく制御する必要性が高まっています。

データベース アプリケーションでこのような要件を満たすための一般的なアプローチは、アプリケーション コードに必要なロジックを実装することです。たとえば、n 階層アプリケーションのビジネス ロジック層で、フィルタ処理を適用する方法があります。または、2 階層のクライアント/サーバー アプリケーションでは、クライアントがフィルタ処理を適用することもできます。このアプローチは、クライアント/サーバー アプリケーションには効果的なこともありますが、データが実際に保護されるわけではありません。Microsoft Access または SQL のクエリ ツールを使用してバックエンド データベースに接続したユーザーは、自分が SELECT アクセス許可を持っているテーブル内のすべての行に無制限でアクセスできます。

他の一般的なアプローチとして、すべてのデータ アクセスをストアド プロシージャでラップする方法があります。このアプローチでは、前の段落で述べた問題が軽減されます。この場合、ユーザーは基になるテーブルに関してすべてのアクセス許可が拒否され、代わりにフィルタ処理ロジックを実装するストアド プロシージャの実行権限が与えられます。この方法にも特有の短所があります。たとえば、このようなデータベースに対する特別なユーザー レポートは困難で、場合によっては不可能です。ここで必要となるのは、ユーザーに基づいて自動的に適用されるフィルタ処理ロジックを使用し、実際のテーブル (またはビュー) をユーザー アカウントに対して提示する方法です。この場合、すべてのユーザーが Patient テーブルにアクセス権を持っていても、SELECT * FROM Patient を各ユーザーが実行すると、そのユーザーが表示できるデータのみが返されます。このような機能を実現する SQL Server 2005 ベースのソリューションを調べる前に、データに細かいアクセス制御を定義する一般的な方法論である、セキュリティ ラベルについて紹介します。

セキュリティ ラベルの概要

セキュリティ ラベルは、データ項目 (オブジェクト) の秘密度を示す情報の一部です。また、1 つ以上のカテゴリから由来するマークを含む文字列です。ユーザー (サブジェクト) は、同じマークで記述されたアクセス許可を保持します。基本的に、サブジェクトは固有のラベルを持ちます。サブジェクトのラベルはオブジェクトのラベルと比較され、そのオブジェクトへのアクセス権が判断されます。

たとえば、次のテーブルの分類列には、セキュリティ ラベルで注釈を付けられた行があります。

ID

名前

分類

1

John Doe

SECRET

2

Frank Jones

TOP SECRET

3

Sam Barnes

UNCLASSIFIED

このデータを含むシステムに、次のようなユーザー アカウントが含まれているとします。

User

認可

Alice

SECRET

Bob

UNCLASSIFIED (認可なし)

各ユーザーの認可 (セキュリティ ラベルで表されます) によって、テーブルの中でアクセスできる行が決まります。Alice がこのテーブルで SELECT * FROM <テーブル名> を発行すると、次の結果を取得します。

ID

名前

分類

1

John Doe

SECRET

3

Sam Barnes

UNCLASSIFIED

Bob が SELECT * FROM <テーブル名> を発行した場合、取得する結果は次のように異なります。

ID

名前

分類

3

Sam Barnes

UNCLASSIFIED

アクセス制御はこの例よりも複雑になる場合があります。セキュリティ ラベルで表されるアクセス基準は複数存在することがあるためです。たとえば、分類レベルとは別に、あるプロジェクト チームのメンバはデータの一部を表示のみできます。このグループが PROJECT Q いう名前であるとして、次の例を考えてみます。

ID

名前

分類

1

John Doe

SECRET, PROJECT Q

2

Frank Jones

TOP SECRET

3

Sam Barnes

UNCLASSIFIED

ユーザー アクセス許可も変更します。

User

認可

Alice

SECRET、PROJECT Q

Bob

UNCLASSIFIED (認可なし)

Charlie

TOP SECRET

ここでは、TOP SECRET という認可を持つユーザー Charlie を追加しました。また、Alice のラベルに PROJECT Q のマークを追加しました。

これにより、Alice が SELECT * FROM <テーブル名> を発行すると、次のような結果になります。

ID

名前

分類

1

John Doe

SECRET, PROJECT Q

3

Sam Barnes

UNCLASSIFIED

Charlie が SELECT * FROM <テーブル名> を発行すると、次のような結果になります。

ID

名前

分類

2

Frank Jones

TOP SECRET

3

Sam Barnes

UNCLASSIFIED

Charlie には TOP SECRET という認可はありますが、PROJECT Q のマークはないため、行 1 は表示できません。一方、Alice は SECRET のマークと PROJECT Q のマークを両方とも満たしているため、行 1 を表示できます。行 2 は TOP SECRET の認可が必要なので、Charlie のみが表示できます。

基本的なこのアプローチは、マークを追加して応用できます。実際の場合、セキュリティ ラベルには異なるカテゴリのマークが複数含まれることがあり、可能なラベルの組み合わせ数は膨大になることも考えられます。

用語

以降の説明を理解できるように、ラベルとラベルの比較を正確に記述するために必要な用語をいくつか紹介します。ラベルは、オブジェクトの秘密度、またはサブジェクトのアクセス許可を記述する文字列です。ラベルは、複数のマークが集まったものです。各マークは、特定のアクセス許可を記述します。ラベルのマークは、1 つ以上のカテゴリに由来します。

前述の例について分析します。

ラベル

マーク

カテゴリ

SECRET,PROJECT Q

SECRET

分類

 

PROJECT Q

プロジェクト チーム

サブジェクトは、サブジェクト ラベルがオブジェクト ラベルより優位である場合に、そのオブジェクトにアクセスできます。A と B という 2 つのラベルがあるとして、ラベル B に含まれるカテゴリすべてについて、ラベル A のマークが満たしている場合、ラベル A がラベル B より優位と言います。マークがカテゴリを満たしているかどうかは、各カテゴリの属性によって変わります。この例の場合、各カテゴリは次の属性で特徴付けられます。

属性

説明

ドメイン

カテゴリで可能なマークです。

階層構造 (yes または no)

階層型のカテゴリには値の順序付けがあり、これによりアクセスが決定します。マークは、そのマーク自体の階層レベル以下にあるマークを満たすことができます。

非階層型のカテゴリには、値の順序付けがありません。マークはある場合とない場合があります。

基数

1 つのオブジェクトに適用できるドメインの値の数です。

比較規則

サブジェクトが ANY または ALL のマークをカテゴリのオブジェクトに適用する必要があるかどうかを示します。

わかりやすいように、例を挙げて説明します。2 つのカテゴリを持つセキュリティ ラベル スキームがあるとします。

カテゴリ

ドメイン

階層構造

基数

比較規則

分類

TOP SECRET
SECRET
CONFIDENTIAL
UNCLASSIFIED

Yes

1..1
(1 のみ)

ANY

区画

Q
BN
G
K

No

0..*
(0、1、または多数)

ALL

ラベルについて例を挙げて説明します。いずれの場合も、ラベル A がラベル B より優位であるかどうかが問題となります。

例 1

ラベル A

SECRET,Q

ラベル B

SECRET,Q,G

このようなラベルを比較するには、各カテゴリのマークを比較する必要があります。

分類   A の SECRET マークは、B の SECRET マークを満たします。
区画   B の全コンポーネント (ALL) が A に含まれる必要があるため、A の Q 区画は B の Q,G 区画を満たしていません。

したがって、A は B より優位ではありません。

例 2

ラベル A

TOP SECRET,Q,G,BN

ラベル B

CONFIDENTIAL,Q,G

分類   A の TOP SECRET マークは、B の CONFIDENTIAL マークを満たします。
区画   B の全コンポーネント (ALL) が A に含まれるため、A の Q,G,BN 区画は B の Q,G 区画を満たしています。

したがって、A は B より優位です。

例 3

ラベル A

SECRET,Q,K

ラベル B

CONFIDENTIAL

分類   A の SECRET マークは、B の CONFIDENTIAL マークを満たします。
区画   B の区画はありません。つまり、必須の区画はありません。

したがって、A は B より優位です。

行レベルおよびセル レベルのアクセス制御に対するセキュリティ ラベル

セキュリティ ラベルは、公的機関、民間企業を問わず多くの環境で利用されています。マルチレベル保護による (MLS : Multi-Level Secure) 情報管理領域 (システムで複数の秘密度レベルを使用して安全に情報を管理する領域) は、1970 年代以降、セキュリティ ラベルに基づいて発展してきました。そのため、ここで紹介する設計は、データとユーザーに対してアクセス許可を定義する方法論としてのセキュリティ ラベルに基づいています。

データベース ソリューションの概要

次に説明する設計では、ラベルに基づいて行レベル セキュリティとセル レベル セキュリティを SQL Server 2005 データベースに追加する方法の詳細について紹介します。簡単に説明すると、この設計の目的は、任意のラベル付けスキームを定義し、SQL Server 2005 機能でそのスキームを実施することです。

この設計により次のことが実行されます。

  1. 任意のラベル付けカテゴリとマークを定義するための構造を追加します。

  2. 基本的なマーク (Top Secret と Confidential、USA、UK、Taskforce Z など) に対して、ロール メンバシップによってユーザーが直感的にラベルを定義できるようにします。

  3. データを書き込むときの write-up、write-down などの制御モデルを提供します。

  4. データベース内で暗号化を選択的に利用し、セル レベル セキュリティを実現します。それによって、SQL Server 2005 の完全に内部的な自己管理の証明書ストアを有効に活用します。

  5. 開発者または管理者による基本的な入力の選択肢を前提にして、実装の多くを自動化するツールの開発など、定型的なガイドラインを示します。

  6. 何社かのベンダから販売されている行レベル セキュリティ ソリューションに共通する行レベル セキュリティの脆弱性のうち、特定の脆弱性を回避する方法を提示します。

行レベル セキュリティ

行レベル セキュリティを強制適用するためにこのドキュメントで使用するメカニズムは、SQL Server のビューです。ビューを使用すると、定義済みのクエリをテーブルのようにユーザーまたはアプリケーションに示すことができます。ビューへのアクセス権はユーザーに付与できますが、基になるテーブルへのアクセスは拒否されます。こうすることで、ユーザーがビューを経由せずにベース テーブルに直接アクセスすることを回避します。ここでは、特殊な構成のビューを使用し、ラベルに基づいた行レベル セキュリティを実施するために必要なロジックをすべて適用しています。

場合によっては、複雑なクエリ (レポート用など) を、クエリの実行が簡単な単一データベース オブジェクトにするための前処理段階で、ビューを使用することがあります。ただし、このような前処理はここでは実行しません。この設計の目的は、単にビューのベース テーブルをほとんど同じ定義でラップすることです。ユーザー (またはアプリケーション) は、ビューをテーブルのように扱って、ビューのクエリまたは更新を実行し、別テーブルに結合することもできます。ただし、ベース テーブルへのアクセスは拒否されます。

このビューを作成するには、次の 4 つの処理が必要です。

  • ラベルのカテゴリとマークを定義するテーブルを作成し、一意のセキュリティ ラベル組み合わせに対してプロパティをそれぞれ割り当てます。この処理は各データベースで 1 回のみ実行する必要があります。

  • ラベルをマークするロールを作成します。このロールは、ユーザーに割り当てるラベルを表すときに使用されます。この処理も各データベースで 1 回のみ実行する必要があります。

  • ベース テーブルにいくつかの変更を加えます。

  • ビューを定義します。

ラベル付けスキームの定義

まず、ラベルのメタデータを定義するテーブルをいくつか作成します。図 1 の ER 図に、作成するテーブルを示します。tblCategory テーブルでは、ラベルに含めることができるカテゴリを定義します。カテゴリごとに可能な値 (カテゴリのドメイン) が指定されます。 この値は tblMarking テーブルで定義されます。カテゴリが階層構造の場合、親子関係もこのテーブルで定義されます。これらのテーブルの列は、前述したセキュリティ ラベル カテゴリに基づいているため、ここでは説明を省略します。

Cc966395.SQLServe1001(ja-jp,TechNet.10).gif

図 1
拡大表示する

次の SQL Server ステートメントに、ラベル付けスキーム例を設定する方法を示します。

--Categories
INSERT tblCategory (ID, Name, CompareRule, DefaultRole) 
VALUES (1, 'Classification', 'ANY', NULL)

INSERT tblCategory (ID, Name, CompareRule, DefaultRole) 
VALUES (2, 'Compartment', 'ALL', 'public')

INSERT tblCategory (ID, Name, CompareRule, DefaultRole) 
VALUES (3, 'Nationality', 'ANY', 'public')

INSERT tblCategory (ID, Name, CompareRule, DefaultRole) 
VALUES (4, 'Need-to-Know', 'ANY', 'public')
GO

--Classification markings
INSERT tblMarking (CategoryID, RoleName, MarkingString, 
ParentCategoryID, ParentRoleName) VALUES (1, 'T', 'T', NULL, NULL)
INSERT tblMarking (CategoryID, RoleName, MarkingString, 
ParentCategoryID, ParentRoleName) VALUES (1, 'S', 'S', 1, 'T')
INSERT tblMarking (CategoryID, RoleName, MarkingString, 
ParentCategoryID, ParentRoleName) VALUES (1, 'C', 'C', 1, 'S')
INSERT tblMarking (CategoryID, RoleName, MarkingString, 
ParentCategoryID, ParentRoleName) VALUES (1, 'U', 'U', 1, 'C')

--Compartment markings
INSERT tblMarking (CategoryID, RoleName, MarkingString) 
VALUES (2, 'Q', 'Q')
INSERT tblMarking (CategoryID, RoleName, MarkingString) 
VALUES (2, 'G', 'G')
INSERT tblMarking (CategoryID, RoleName, MarkingString) 
SELECT 2, DefaultRole, 'none' FROM tblCategory WHERE ID = 2

Etc.…

次に、tblUniqueLabel テーブルに注目してください。このテーブルは、特定の組み合わせのマーク (特定のセキュリティ ラベル インスタンス) を一意の ID に対応付けるときに使用されます。効率性を考慮し、このテーブルは "必要に応じて" 設定します。特定のセキュリティ ラベルを持つデータがデータベースに追加されたときは、ストアド プロシージャが呼び出され、この一意のラベルを表す ID が取得されます。tblUniqueLabel に対応する行がない場合、新しい行が追加され、新しい ID が返されます。このテーブルには、ちょうど必要な数の行 (多くも少なくもない) だけを設定します。

最後に、tblUniqueLabelMarking テーブルでは、個々のマーク値とセキュリティ ラベル インスタンスとを関連付けます。

ロール

ラベル付けスキームを定義した後は、SQL Server のセキュリティ モデルに結びつける必要があります。定義した全カテゴリのマークごとに、対応するデータベース ロールを作成します。

メモ   Windows 統合認証を、Windows ユーザー アカウントに対応付けられた SQL ログインと共に使用している場合、Windows グループも使用できます。このホワイト ペーパーで説明するデータベース ロールには、Windows グループを使用する方法を適用できます。

データベース ロールは以下のガイドラインに従う必要があります。

  • ANY または ALL の比較規則を使用する非階層構造のカテゴリの場合、考えられる値ごとにロールを作成します。ロールの名前は、そのマークに関する tblMarking の RoleName 列の値と一致する必要があります。

  • 階層構造のカテゴリの場合も同じ処理を実行します。ただし、追加の処理として、ロールを入れ子にして階層をモデル化する必要があります。作成するロールごとに、定義された親ロールを tblMarking テーブルにメンバとして追加します。入れ子にすることで、たとえば、Secret の認可を持つユーザーが、Secret レベル以下のデータにアクセスできるようになります。

これらのロールを使用することで、ユーザーがアクセスできるセキュリティ ラベルを正確に示すアクセス許可を、ユーザーに付与できます。システムのユーザーに関するロール メンバシップを正確に維持することは、アプリケーションまたは DBA の役割です。

次のセクションでは、ロール メンバシップを使用して、ユーザーがアクセスできるデータを特定する方法について説明します。

確認できる内容

アプリケーション テーブルに注目する前に、実際の行レベル セキュリティ ロジックをカプセル化するヘルパ ビューを作成します。このビューを vwVisibleLabels と呼びます。このビュー定義は次のように始まります。

SELECT ID, Label.ToString() 
FROM tblUniqueLabel WITH (NOLOCK)
WHERE ….

WHERE 句の定義は、ラベル付けスキームのカテゴリ属性に基づいています。重要な属性は比較規則です。ANY の比較規則を含むカテゴリごとに、次に示すように述語を WHERE 句に追加します。

ID IN (SELECT ID FROM tblUniqueLabelMarking WITH (NOLOCK) 
WHERE CategoryID = <HardCodedCatID> AND IS_MEMBER(MarkingRoleName) = 1)

この句によって、現在のユーザーがメンバである特定のカテゴリから、マークを含む一意のセキュリティ ラベル ID がすべて取得されます。さらに詳しく説明します。サブクエリによって、特定のカテゴリ内の行について、tblUniqueLabelMarking テーブルがスキャンされます。このような行のうち、現在のユーザーが、MarkingRoleName 列で設定されているデータベース ロールのメンバである行が選択されます。このチェックは、SQL Server 固有の関数 IS_MEMBER によって実行されます。この処理結果の各行について、tblUniqueLabel 内の対応するレコードの ID が外部クエリに返されます。

次に、ALL の比較規則が指定されたカテゴリの場合について説明します。ALL の比較規則を含むカテゴリごとに、次に示すように述語を WHERE 句に追加します。

1 = ALL(SELECT IS_MEMBER(MarkingRoleName) FROM tblUniqueLabelMarking (NOLOCK) 
WHERE CategoryID = <HardCodedCatID> AND UniqueLabelID = tblUniqueLabel.ID)

この述語の結果は、ユーザーがアクセスするには割り当てられたマークをすべて持つ必要があるという比較になります。サブクエリでは、tblUniqueLabel の特定のレコードに関連するマークすべてについて IS_MEMBER が返す値の一覧を取得します。この戻り値がすべて 1 の場合、述語は満たされます。述語を追加する場合、項目間に AND 演算子が必要です。

このドキュメントで前述した例をまとめると、次のような中間のビュー定義になります。

CREATE VIEW vwVisibleLabels
AS

SELECT ID, Label.ToString() 
FROM tblUniqueLabel WITH (NOLOCK)
WHERE
ID IN --Classification
(SELECT ID FROM tblUniqueLabelMarking WITH (NOLOCK) 
WHERE CategoryID = 1 AND IS_MEMBER(MarkingRoleName) = 1)
AND   --Compartments
1 = ALL(SELECT IS_MEMBER(MarkingRoleName) FROM tblUniqueLabelMarking
WHERE CategoryID = 2 AND UniqueLabelID = tblUniqueLabel.ID)

GO

このビューを vwVisibleLabels と呼びますが、"自分 (現在のユーザー) がアクセスできるデータベースに存在するセキュリティ ラベルすべての一覧" と考えることができます。

次の表に、ラベル付けスキームを設定する場合の設計規則の概要をまとめます。この規則は各カテゴリの属性によって変わります。

Cc966395.SQLServe1007(ja-jp,TechNet.10).gif

拡大表示する

ベース テーブルへの変更

次に、行レベル セキュリティを追加するベース テーブルに加える必要のある変更について説明します。変更はそれほど多くありません (後述するセル レベル セキュリティの場合は、変更は多くなります)。RowLabel と RLSMappingID という 2 つの列をベース テーブルに追加します。RowLabel は、その行に関する未加工のセキュリティ ラベルです。RLSMappingID は、そのラベルに対応する tblUniqueLabel に含まれる整数の ID です。

厳密に言うと、必要なのは RLSMappingID のみです。実際のところ、RowLabel 列を含めることは、第 3 正規形に違反します。ただし、厳密なセキュリティが求められる状況では多くの場合、データがデータベースに追加されたときにそのデータに付随したセキュリティ ラベルが、データと共に常に保持される必要があります。そのため、ここではベース テーブルに RowLabel を含めています。場合によっては、行に関する他の未加工ソースのメタデータについて、別の列を追加する必要があります。特定のアプリケーションにどのようなポリシーを指定する場合でも、ベース テーブルに必須のフィールドは RLSMappingID のみです。

この時点で次に行われる処理はすぐにわかります。セキュリティ ラベルを含む行がベース テーブルに挿入されると、tblUniqueLabel でそのラベルに対応する ID が取得 (または生成) され、新しい行の RLSMappingID 列に指定されます。同様に、セキュリティ ラベルを変更するために行を更新すると、RLSMappingID も変更されます (セキュリティ ラベルを更新できるかどうかは、個々の状況のセキュリティ要件によって変わり、明示的に禁止されることもあります)。

最後に、ベース テーブル上の RLSMappingID と、tblUniqueLabel 内の ID 列との間に、外部キーの関連付けを作成します。

パフォーマンス上の理由から、RLSMappingID 列に非クラスタ化インデックスを作成します。この処理を省略すると、パフォーマンスが低下します。

ビューの定義

これで最終的な手順の準備が整いました。ここでは、ベース テーブル上にビューを作成します。これによって、エンド ユーザーまたはアプリケーションからは実質的にテーブルが置き換えられたように見えます。ビュー定義は次のようになります。

CREATE VIEW UserTable
AS

SELECT <base table column list which does not include RLSMappingID, or any columns from vwVisibleLabel>
FROM tblBaseTable, vwVisibleLabel
WHERE tblBaseTable.RLSMappingID = vwVisibleLabel.ID

GO

GRANT SELECT ON UserTable TO <app_users>
DENY ALL ON tblBaseTable TO <app_users>
GO

これで、ラベルに基づいて行レベル セキュリティを透過的に強制適用するビューが作成されました。このビューではアプリケーション ロジックは使用せず、アプリケーション レイヤが省略された場合でもその効果は維持されます。

当然ながら、このビューはテーブルから行を選択する場合にのみ有効です。このテーブルを使用するアプリケーションが行の挿入、更新、または削除を行う場合、他にもいくつかの処理が必要となります。

挿入、更新、および削除

ここまで説明してきたのはすべて、基になるテーブルから行を SELECT する場合の説明でした。アプリケーションではテーブルに情報を書き込む場合もあります。ラベルに基づく行レベル セキュリティが指定されているテーブルで、行の挿入、更新、または削除を行う場合、いくつかの疑問が出てきます。ユーザーが更新できるのはどの行でしょうか。新しく挿入された行に適用できるセキュリティ ラベルのレベルに、何か制約はあるでしょうか。

結局のところ、答えは状況によって変わります。あるシステムでは "read-down, write-down" 動作のみを許可しても、別のシステムでは "read-down, write-up" 動作のみを許可する場合があります。いずれの場合も、ほとんど同じ技術を使用してサポートできます。

ベース テーブルへの挿入と更新をサポートするには、次の処理を実行する必要があります。

  • ユーザーがアクセスできるビューへの挿入または更新を許可します。

  • 有効な行ラベルを要求します。

  • 新しいラベルのマッピング ID を必要に応じて生成するか、ラベルを既存の ID に対応付けます。

  • 状況に応じて、write-down ロジックまたは write-up ロジックを実行します。

  • ベース テーブルへの挿入/更新を実行します。

これは、挿入および更新の instead-of トリガを定義することで実現します。このトリガは、ラベルの有効性をチェックし、ラベルのマッピング ID を生成または取得し、書き込みアクセス許可のチェックを実施し、ベース テーブルへの実際の挿入または更新を処理します。

この処理を実行する instead-of トリガの例については、「付録 1」を参照してください。このコードにはいくつか注意する点があります。

まず、次の 2 行を見てください。

DECLARE @RowClassification SecurityLabel
SELECT @RowClassification = row_label FROM inserted

これによって、INSERT ステートメントに含まれていた行のラベルが、SecurityLabel というユーザー定義型のインスタンスに取り込まれます。SecurityLabel は、特定のラベルを構成する値をカプセル化します。SecurityLabel インスタンスは、Dominates 関数 (C# のユーザー定義関数) と比較できます。この方法を使用すると、2 つのラベルを比較して、一方が優位かどうかを確認できます。ここでは、この方法をトリガに使用して、現在のユーザーのアクセス許可と、挿入するデータのラベルとを比較しています。

ただし、この比較を行う前に、現在のユーザーのアクセス許可が記述された SecurityLabel のインスタンスを取得する必要があります。この場合、次の行を使用します。

EXECUTE AS CALLER
SELECT @CallerName = CURRENT_USER
REVERT 
DECLARE @UserClearance SecurityLabel
SET @UserClearance = dbo.GetUserLabel(@CallerName)

GetUserLabel は C# で記述されたユーザー定義関数で、現在のユーザーのロール メンバシップを検査し、そのアクセス許可レベルを記述したラベルを生成します。ラベルは SecurityLabel のインスタンスとして返されます。このインスタンスを使用して、ユーザーのアクセス許可を任意のラベルと比較できます。この例の場合、次のことを適用する write-down-only 要件を実装します。

IF dbo.Dominates(@UserClearance, @RowClassification) = 0
RAISERROR('user rights not sufficient to write this data', 12, 1)

write-up 要件を適用するときにも同様のコードを使用できます (または、ユーザーの認可レベルを使用した挿入について、何も表示しないでデータにラベル付けすることを要件で求める場合、Dominates のチェックを省略し、単に GetUserLabel から返されるラベルを使用することもできます)。

ユーザーがデータの書き込みアクセス許可を持っていると仮定した場合、その行ラベルのマッピング ID を取得する必要があります。ストアド プロシージャ usp_GetRLSMappingID がこの取得を実行します。このプロシージャは、既存 ID の取得、または新しい ID の生成という作業を、必要に応じて行います。

マッピング ID を使用して、ベース テーブルへの挿入処理に進むことができます。

更新または削除の instead-of トリガのコードでも同様のロジックを使用します。

外部キー

行レベル セキュリティの話題を終える前に、RLS で保護されたテーブルの参照という、テーブルの問題を解決する必要があります。あるテーブルの行が別のテーブルに含まれる制限付きの行を参照している場合、参照している側の行をユーザーが表示できると、情報が漏洩することになります。この問題は、ベース テーブルを変更することで対処できます。RLSMappingID を追加するときに、この列を主キーに追加できます。別のテーブルにある主キーを参照する場合は、行レベルの制御も必ず行い、また行レベルの制御をそのテーブルに関するビュー定義に使用できます。

各設計の統合 (第 1 部)

ここで示した設計には多様な側面がありますが、複数層の設計パターンに統合して、ラベルに基づく行レベル セキュリティを既存のテーブルに設定できます。これを図 2 にわかりやすく示します。最下層はベース テーブルです。ここではベース テーブル上にビューを作成し、ビューにより、テーブルの読み取りに関する行レベル セキュリティを適用します。図の横に示したサポート対象メタ情報によって、ビューは変わります。

この RLS ビューの上に、このビューから直接選択する外部ビューを作成します。また、このビューに instead-of トリガも含め、テーブルでの挿入、更新、または削除をサポートします。厳密にはこれら 2 つのビューを分ける必要はありませんが、セル レベル セキュリティを追加する場合は、このようなモデルが必要になります。

Cc966395.SQLServe1002(ja-jp,TechNet.10).gif

図 2
拡大表示する

最上部のビューと vwVisibleLabels ビュー (図の灰色の部分) のみがユーザー ログインで公開されます。

この複数層の設計パターンには、次のようにいくつかの利点があります。

  • 主な設計ポイントが、単純で扱いやすい要素に分割されています。

  • 所定のテーブルに設計を適用するプロセスが定型的で機械的です。

  • アプリケーション コードやストアド プロシージャではなく、コア構造に書き込む場合にセキュリティ ラベルのロジックをカプセル化します。

以降のセクションでは、セル レベルのセキュリティを含めるようにこの設計パターンを改善し、(オプションで) 異なる秘密度を持つデータを個別の I/O デバイスに物理的に分割します。

セル レベル セキュリティ

行全体の制御よりもきめ細かいレベルの制御がデータに必要な場合があります。たとえば、大半の行をユーザー グループが表示できる一方で、秘密度の高い一部のセルを表示するために追加のチケットが必要な場合です。この例の概念を図 3 に示します。模様の違いは、そのデータに割り当てられているラベルが異なることを示します。下から 2 行目は行レベル セキュリティで制御されていますが、テーブルに固有のラベルが設定されているセルが点在しています。

図 3

図 3

ユーザーのアクセス許可に基づいてデータの可視性を本質的に制御し、しかもできるだけデータの近くで制御できるのが望ましいやり方です。理想は、データベース エンジンが、ラベル付きセルのデータを表示するかどうかを、接続したユーザー ID に基づいて単純に決定できることです。ただし、このようなモデルをサポートする簡単な方法は現時点ではありません。

SQL Server 2005 では、データベース エンジン自体に暗号化機能を組み込みました。この機能を使用して、任意のデータの暗号化と暗号化の解除を行うことができます。このとき、データベース エンジンで内部的に管理される証明書とキーのインフラストラクチャが使用されます。外部ソースから証明書やキーを渡す必要はありません。

ここでは、理想的なレベルのセル レベル制御を実現する設計を提案しています。ある意味では実装と管理が比較的容易です。

この設計の基本的な目的は以下のとおりです。

  • セルのデータへ任意にラベル付けする機能のサポート。

  • ユーザーのラベルを動的に評価し、適切なセルのみを表示すること。

  • サイズが大きい場合でもパフォーマンスの影響が許容範囲であること。

SQL Server 2005 での暗号化

SQL Server には、内部的な関数があり、証明書、非対称キー、または対称キーを使用して、簡単にデータの暗号化と暗号化の解除を行うことができます。これらのデータは、すべて内部的な証明書ストアで管理します。証明書ストアでは、あるレベルの証明書とキーを上の階層で保護するという、暗号化の階層構造を使用します。SQL Server 2005 ではこのような機能領域のことを "シークレット記憶域" と呼びます。

図 4

図 4 (出典 : 「SQL Server 2005 Books Online」)

内部の API でサポートされている最速の暗号化モードは、対称キーの暗号化です。このモードは、大規模なデータを処理するときに適しています。対称キーは、X509.v3 証明書で暗号化されて格納されます。キー ストアを保護する暗号化の階層を図 4 に示します。

SQL Server 2005 では、いくつかの対称キー暗号化アルゴリズムをサポートしています。これらのアルゴリズムは、Windows Crypto API で実装されています。サポートされているアルゴリズムと各アルゴリズムのキー サイズについては、「付録 2」を参照してください。

SQL Server 2005 は、開いている対称キーをデータベース接続のスコープ内に複数維持できます。"開いている" とは、ストアから取得し、データの暗号化解除に使用できる状態を指します。一部のデータの暗号化を解除するときに、使用する対称キーを指定する必要はありません。正しいキーが既に暗号化を解除され現在開いている場合、データベース エンジンは、開いている対称キーに暗号化されたバイト ストリームを対応させることができます。このキーは暗号化を解除するときに使用され、データを返します。正しいキーが開いていない場合、NULL が返されます。

キーを "開く" 機能は、キーに対する ACL に直接依存します。

このような SQL Server 2005 暗号化メカニズムをサポートしていることを前提に、以下のアプローチについて検討します。

  • データベースのデータにマークするときに使用する一意の各ラベルについて、対称キーを作成します。

  • ラベル付きのセルにあるデータを対応するキーで暗号化します。

  • キーへのアクセスを、ユーザー ラベルの方が優位であるラベルに対応付けられたキーのみ開けるように制御します。接続が確立するときに、このようなキーをすべて開くことができる簡単な方法が実現します。

  • ベース テーブル上のビューを使用して、ビューを定義する SELECT ステートメントに、暗号化解除 API への呼び出しを含めます。

このようなビューの簡単な定義例を以下に示します。

CREATE VIEW MyTable
AS

SELECT ID,
DecryptByKey(SensitiveData),
DecryptByKey(OtherSensitiveData),
NonSensitiveData
FROM BaseTable

GO

このアプローチを前提に、ユーザーがビューで選択したときの結果について説明します。ユーザーがアクセスできるラベルに対応付けられたキーは、すべて開いた状態になります。そのため、ユーザーがアクセスできるラベルを含むセルは、SELECT ステートメントを実行するとすべての暗号化が解除されます。反対に、ユーザーがアクセスできないラベルに対応付けられたキーは、いずれも開いた状態にはなりません。この場合、SELECT ステートメントが実行されると、セルは NULL に戻り、ユーザーには何も情報が与えられません。セルにデータがあるかどうかもわかりません。

このアプローチによって、検索対象であるリレーショナル テーブルのデータに対して、より細かい動的な制御を実現できます。

キー アクセス制御

当然ながら、設計の有効性はキーの制御によって決まります。SQL Server 2005 では、単一の SQL Server プリンシパルという観点で、キーにアクセス許可を定義しています。たとえば、キーを開くアクセス許可は、Bob というユーザーに付与することも、AppUsers というグループに付与することもできます。ただし、このドキュメントでは、任意の組み合わせのプリンシパルに基づいて (より正確に言うと、ユーザーのラベルを定義する、任意の組み合わせのロール メンバシップに基づいて)、キーに対する権限を制御しようとしています。可能なラベルの組み合わせすべてについて、プリンシパルを定義することは避けたいところです。ユーザーがアクセスできるキーを決定するセマンティクスは、行のアクセスを制御する場合と同じです。

キーに対するこの種のアクセス制御には、より微妙なアプローチが必要です。キーのアクセス許可をユーザーやロールに付与するのではなく、ここではシステム定義のブローカ ユーザー アカウントにアクセス許可を付与します。これを KeyBroker アカウントと呼びます。KeyBroker はキーを開くことができます。ユーザーとユーザー ロールは、キーに対して何もアクセス許可が付与されません。ここでは、ユーザーがアクセス権を持つラベルの一覧を取得し、そのラベルに対応するキーを開くことを KeyBroker に要求します。

SQL Server 2005 の新しい偽装機能を使用すると、ユーザーから呼び出すことができるストアド プロシージャを定義できますが、これは KeyBroker として実行されます。このプロシージャを以下に示します。カーソルは vwVisibleLabel ビューで定義されます。このビューの 2 つの列 KeyName と CertName については、まだ説明していません。カーソルはこれら 2 つの列をビューから選択します。別の新しい偽装機能を使用して、呼び出し元ユーザーの ID へと一時的に戻し、カーソルを開きます。こうすることで、vwVisibleLabels から返される行は、呼び出し元ユーザーのラベル (つまり、ロール メンバシップの組み合わせ) に基づいたものになります。この後、すぐに KeyBroker の ID へと戻します。残りのプロシージャではカーソル内の行をループし、暗号化に使用された専用の証明書を使用して各キーを開きます。

CREATE PROCEDURE usp_EnableCellVisibility
WITH EXECUTE AS 'KeyBroker'
AS
DECLARE @KeyName nvarchar(256)
DECLARE @CertName nvarchar(256)

DECLARE Key_Cursor CURSOR LOCAL FORWARD_ONLY STATIC FOR
SELECT KeyName, CertName
FROM vwVisibleLabels

EXECUTE AS CALLER
--Since the cursor is STATIC, it is fully 
--populated here based on the caller’s identity
OPEN Key_Cursor  
REVERT

FETCH NEXT FROM Key_Cursor INTO @KeyName, @CertName
WHILE @@FETCH_STATUS = 0
BEGIN
open symmetric key @KeyName using certificate @CertName
FETCH NEXT FROM Key_Cursor INTO @KeyName, @CertName
END

CLOSE Key_Cursor
DEALLOCATE Key_Cursor

GO

このストアド プロシージャを終了すると、ユーザー コンテキストは自動的に呼び出し元ユーザーに戻ります。このアプローチでは、ユーザーのラベルの方が優位であるラベルと関連付けられているキーのみが開きます。ユーザーにはキーへのアクセス権が何もないため、何も開くことはできません。また、このプロシージャになりすましで入力することはできません。権限の評価は、SQL Server データベースのロールのメンバシップにのみ基づいています。

正しい対称キーが開いている状態でビューから選択すると、ユーザーのラベルの方が優位であるラベル付きセルが表示されます。その他のラベル付きセルは、NULL として表示されます。

おそらく、データベース接続が確立した直後に、アプリケーションまたはエンド ユーザーによって、このストアド プロシージャ (usp_EnableCellVisibility) が 1 回呼び出されます。このとき、開いているキーは、接続が有効である間は開いたままとなります。対応するプロシージャ (usp_DisableCellVisibility) は、必要な場合にキーを閉じるために用意されています。接続を閉じると必ずキーも閉じられるため、このプロシージャは必ずしも必要ではありません。

ベース テーブルへの変更

セル レベル セキュリティをサポートするベース テーブルへの変更点はわずかです。最も重要な変更点は、保護する列のデータ型に、暗号化されたデータ値との互換性が必要なことです。組み込み関数 EncryptByKeyvarbinary を返します。この結果は、文字フィールドまたはバイナリ フィールドに格納できます (たとえば、varcharnvarcharvarbinary など)。元のデータ型が varbinary のコンテンツと互換性がない場合 (たとえば int のような数値型など)、列のデータ型を変更する必要があります。次のセクション「ビューの定義」では、ユーザーから見えるデータ型が変わらないようにする方法について説明します。

必須の変更点は、正しいデータ型にすることのみです。もう 1 つの変更点は、セルに適用するラベルを保持する列を追加することで、状況によっては望ましい処理となります。この処理の主な目的は、セル データに付随するラベルのメタデータを維持するというポリシー要件を順守することです。このメタデータは、tblUniqueLabel テーブルの ID、SecurityLabel UDT のインスタンス、またはソース データの行ラベル文字列として格納されることが考えられます。

ビューの定義

最後に、ユーザーがアクセスできるビューを再定義し、保護されたセル内のデータの暗号化を解除するロジックを含める必要があります。以下のコードに、ビューの定義例を示します。この定義例は、行レベル セキュリティを説明したときのビュー定義例とほぼ同じです。唯一の違いは、列の一部が DecryptByKey という SQL Server 2005 固有の関数でラップされていることです。例をわかりやすくするために、ベース テーブルの数列のみを使用しています。

CREATE VIEW UserTable
AS

SELECT ID,
DecryptByKey(SensitiveData),
CONVERT(money, CONVERT(varchar(50), DecryptByKey(SensitiveMoneyData))),
NonSensitiveData
FROM tblBaseTable, vwVisibleLabel
WHERE tblBaseTable.RLSMappingID = vwVisibleLabel.ID

GO

GRANT SELECT ON UserTable TO <app_users>
DENY ALL ON tblBaseTable TO <app_users>
GO

前のセクションで説明したように、暗号化関数では、入力と出力に文字データかバイナリ データを使用します。たとえば、保護された列の元のデータ型が数値の場合、暗号化を解除した varbinary の出力を元のデータ型に変換した値を、ビュー定義に含める必要があります。上記のビューの 3 列目にこの例があります。

挿入/更新時のセル データの暗号化

セル データの暗号化は、行レベル セキュリティの write-up/write-down チェックの処理方法とほぼ同じです。ビューで定義されている instead-of トリガで、write-up/write-down チェックを処理します。また、ラベルに基づいてセルも暗号化します。「付録 1」に、このロジックのコード例を示しています。

物理的な分割

複数レベルの分類にあるデータを組み合わせるシステム設計の場合、物理記憶域のデータが混在する問題に対処する必要があります。ここで示した設計の一部では、分類が異なるデータを各物理記憶域ユニットに分散できるようにする必要があります。そのため、他の側面と同様に、構造的にデータにできるだけ近い、統合された規範的なリファレンス設計の要件を含むソリューションにします。データ管理ソリューション上に構築するアプリケーションでは、この問題に対処する必要はありません。

SQL Server 2005 の別の機能である分割テーブルを使用すると、この要件に対処できます。以前の SQL Server バージョンの分割ビューを使用して対処することもできますが、より困難な作業となります。

テーブル分割機能を使用すると、テーブルの列を使用してデータを相互に関連のない論理パーティションに分割する "分割関数" を定義できます。分割関数は、物理記憶域ユニット (つまり、ファイル) に対応付けられます。このファイルは個別の物理デバイスに配置できます。その他の制御方法としては、各物理ボリュームで Windows EFS (暗号化ファイル システム (EFS)。Windows 2000 以降でサポートされています) のサポートを使用して、物理メディア レベルで強固な暗号化を適用する方法があります (ファイル システム レベルでの暗号化によるパフォーマンスの影響を相殺するために、大量の I/O ハードウェア リソースが使用される場合、このような EFS の使用は控えることをお勧めします)。

各設計の統合 (第 2 部)

セル レベル セキュリティと、前述した行レベル セキュリティ モデルでの物理的な分離とを組み合わせた設計を図 5 に示します。

Cc966395.SQLServe1005(ja-jp,TechNet.10).gif

図 5
拡大表示する

前述したように、ここまで提示してきた技術はさまざまな方法でコードに実装できます。ただし、複数層の設計パターンには、次のようにいくつかの利点があります。

  • 主な設計ポイントが、単純で扱いやすい要素に分割されています。

  • 所定のテーブルに設計を適用するプロセスが定型的で機械的です。メタデータに基づいて、この実装を自動スクリプトするのが望ましい場合もあります。

  • アプリケーション コードやストアド プロシージャではなく、コア構造に書き込む場合に、セキュリティ ラベルのロジックをカプセル化します。

パフォーマンス

この設計を評価する場合、主な問題はパフォーマンスに与える影響です。必要なセキュリティを実装し、さらに数百万単位の行数があるテーブルでもパフォーマンスの影響が許容範囲にある、という解決策が必要です。

この設計の実装例は、100 万行を含むデータセットでテストされました。テスト サーバーの仕様は以下のとおりです。

CPU

2 × 550 MHz

RAM

512 MB

ハード ディスク

4 × 18 GB (各 10,000 RPM)
ファイバ チャネル SAN 上の 1 × 20 GB
(EFS なし)

オペレーティング システム

Windows 2003 Server Standard

データベース

SQL Server 2005

.NET Framework

1.1 (アプリケーション コード用)
2.0 (SQL Server コード用)

テストの詳細については、このドキュメントでは説明しません。ただし、得られた結果の概要を次に示します。

  • 挿入パフォーマンスへの影響   40% 以下のパフォーマンス低下

  • 選択クエリへの影響   10% 以下のパフォーマンス低下

  • 集計クエリへの影響   10~50% 以下のパフォーマンス低下

パフォーマンスの特徴は各アプリケーションによって異なるため、この結果を固定のガイドラインとして扱うことは推奨されません。ただし、このテスト結果は、この設計によるパフォーマンス低下が許容範囲であることを示しています。RAM が限られているローエンド (550 MHz) サーバーに関する影響を測定したことに注目してください。製品アプリケーションのパフォーマンスに同様の影響があった場合、ハードウェア リソースを追加することでパフォーマンス低下を補うことができます。

行レベルの漏洩

前述したビューに基づく行レベル セキュリティ設計には、限定的で固有の脆弱性があります。この設計では、アプリケーション、レポート ツール、または SQL クエリ ツールによる直接接続を使用してデータにクエリが実行された場合でも、アクセス権がないユーザーに行を返すことはありません。ただし、条件によっては、エラー メッセージがスローされたときにデータの内容を推測できることがあります。ここでは、この条件、および漏洩の可能性について説明し、2 つの対処方法を提案します。

述語の評価順序

行レベル セキュリティを実装しユーザーがアクセスできるビューに対して、特定の構成のクエリを発行した場合、本来はユーザーにアクセス権のないテーブルでも、テーブルに情報が存在することを判断できるエラーが返される可能性があります。

たとえば、次のようなクエリです。

SELECT * FROM UserTable --actually a view
WHERE LastName = ‘Smith’ AND LEN(LastName)/0 > 10

現在のユーザーが LastName と Smith が等しい行にはアクセス権を持っておらず、またその他にアクセス権を持っていない行が少なくとも 1 行は存在するとします。内部的には、データベース サーバーのクエリ オプティマイザによって、ステートメント内のビューは各部に分解されます。次に、ビューと実際のステートメントの WHERE 句に含まれるすべての WHERE 句条件に基づいて、クエリ プランが構築されます。ステートメントが正常に実行された場合、情報は漏洩しません。ただし、WHERE 句に無効な述語が含まれる場合 (ゼロによる除算など) は危険です。クエリ オプティマイザで生成されるクエリ プランによって変わりますが、行の可視性を制限する述語の前に、無効な述語が評価される可能性があります。この場合、本来表示されるはずのない、行が存在することを示すエラーがスローされます。

上記のステートメント例ではどのような処理が行われるかを説明します。UserTable において、前述した設計でビューが定義されているとします。オプティマイザから見える実際のクエリは次のようになります。

SELECT * 
FROM BaseTable
WHERE (LastName = ‘Smith’ AND LEN(LastName)/0 > 10)
 AND  BaseTable.RLSMappingID IN
 (SELECT ID FROM tblUniqueLabel
 WHERE ID IN .. /* category predicates */)

オプティマイザは、まず LastName 上のインデックスを使用して結果セットを絞り込むという条件を、他の条件よりも先に評価するとします。LastName='Smith' の行を検索してから、LEN(LastName)/0 > 10 を評価します。この結果、行セキュリティの述語が評価される前に、例外がスローされます。

ユーザーがいずれの行も表示できない場合でも、LastName='Smith' の行が 1 行以上あることがエラー メッセージによって判断できる可能性があります。

この問題は、SQL Server を使用したビューに基づく行レベル セキュリティに固有のものではありません。他のベンダによる行レベル ソリューションでも、背後で述語を挿入する場合、同じ危険性があります。これは、クエリ プランが生成される前に述語が挿入されるためです。

次のような脆弱性が問題になる場合に、満たす必要のある厳密な条件を順守することが重要です。

  1. 不正な SQL クエリを、ユーザーが次の方法により直接発行できる場合。

    • SQL クエリ ツールの使用

    • 不適切な設計のフロントエンド アプリケーションに対する SQL の挿入

  2. テーブル定義について十分な知識があり、クエリを記述できる場合。

  3. クエリ オプティマイザが、行アクセスの述語の前に無効な述語が評価される可能性のあるプランを選択する場合。この問題は、存在するインデックス、テーブルに含まれるデータの分散、ステートメントに含まれる他の述語の性質など、いくつかの変動要素によって発生します。

  4. 生成されたエラーをユーザーが直接見ることができる場合。このような機能は SQL クエリ ツールにありますが、適切な設計のアプリケーションであれば、エラー情報をそのまま返すことはありません。

対策

多くの場合、前述したような脆弱性が悪用されることは少ないため、何か対処が必要なことはほとんどありません。ただし、この脆弱性を確実に軽減する必要がある場合、3 つのオプションが推奨されます。いずれもこのドキュメントで説明した同じ基本設計を使用しますが、補足部分があります。

テーブル値関数

脆弱性をなくす 1 つの方法は、基になるデータに対して行アクセスの述語が完全に適用されてから、ユーザーが指定した述語が適用されるようにすることです。この場合、テーブル値関数 (TVF : Table-Valued Function) を使用します。図 5 を修正し、TVF の役割をわかりやすく示します。

Cc966395.SQLServe1006(ja-jp,TechNet.10).gif

図 6
拡大表示する

行レベル セキュリティを実装するビューが TVF でラップされている点を除くと、図 6 と図 5 は同じです。TVF は、ユーザーがアクセスできるビューからクエリが実行されます。TVF に関する定義は次のようになります。

CREATE FUNCTION [dbo].[fn_MyTable_tvf]()
RETURNS @ret TABLE
(<column specs from underlying RLS view>
)
AS

BEGIN
INSERT @ret
SELECT <columns>
FROM vwRLSView

RETURN
END
GO

TVF は複数ステートメントのテーブル値関数であるということに注意してください。インラインのテーブル値関数の場合、意図した効果は得られません。

行アクセスのビューをラップすることで、ユーザー指定の述語が評価される前に、行アクセスのビューはテーブル変数として完全に実体化されます。これによって脆弱性はなくなります。

このアプローチには、以下の短所があります。

  • 複雑になること。

  • パフォーマンス。このアプローチでは、基になるテーブル全体で行レベル セキュリティを適用し、テーブル変数にバッファしておき、ユーザーがアクセスできるビューからそのバッファに対してクエリを実行します。テーブルのサイズが小さい場合、影響はあまりありません。大規模なデータセットの場合 (行数が数百万単位)、パフォーマンスの影響は深刻です。

暗号化

2 つ目の対策では、セル レベル セキュリティで説明したのと同じ暗号化方針を使用します。この場合も、設計の他のすべての側面については図 5 とほぼ同じであると想定します。ただし、各行のデータに別のレベルの暗号化を追加します。セル レベルの暗号化とは別に、行ラベルと関連付けられた対称キーで、ユーザーがアクセスできる列をすべて暗号化します。こうすることで、クエリ プランにかかわらず、ラベルでアクセスが許可されている場合にのみ、データ値を読み取ることができます。

たとえば、4 つの列がある単純なテーブルがあるとします。このテーブルは、図 5 で示したように行レベル セキュリティとセル レベル セキュリティで保護されています。ある行は、次のようなコンテンツです。

列 1

列 2

列 3

列 4

RLSMappingID

Data1

E(Data2, K1)

Data3

E(Data4, K2)

19

この行には、行レベルのアクセス制御のためにラベルが付けられています。そのラベルは、tblUniqueLabel に含まれる ID が 19 のレコードに対応付けられています。また、列 2 と列 4 は、セル レベル セキュリティで保護されています。例に示した行では、列 2 はラベル 1 (K1) のキーで暗号化され、列 4 はラベル 2 (K2) で暗号化されます。ラベルが何を示すかは、この例では問題ではありません。

この対策では追加の暗号化を適用するため、基になるテーブルに格納されるデータは次のようになります。

列 1

列 2

列 3

列 4

RLSMappingID

E(Data1, K19)

E(E(Data2, K1), K19)

E(Data3, K19)

E(E(Data2, K2), K19)

19

ユーザーがアクセスできる各列は、ラベル 19 (K19) に対応付けられた対称キーで暗号化されます。この作業は instead-of トリガで行われます。図 5 でのユーザーがアクセスできるビューには、暗号化を解除するステートメントが含まれていますが、これを修正して暗号化の解除をさらに追加します。

CREATE VIEW UserTable
AS
SELECT DecryptByKey(Column1), 
DecryptByKey(DecryptByKey(Column2)),
DecryptByKey(Column3),
DecryptByKey(DecryptByKey(Column4))
FROM vwRLSView
WHERE ………… --omitted for clarity

セル レベル セキュリティで開いているキーを管理する場合と同じプリンシパルをここでも適用できます。ユーザーのラベル (ロールのメンバシップ) が適切な場合にのみ、K19 は開かれます。そのため、クエリ プランからアクセス許可のないデータにアクセスしたり、情報を漏洩したりする可能性はありません。

このアプローチにも次のような短所があります。

  • 複雑になること。

  • パフォーマンス。より多くの CPU 時間が、暗号化/暗号化解除のために消費されます。また、検索できる列はすべて暗号化されるため、SQL インデックスを使用してクエリを高速化できません。このアプローチにはこれらの問題がありますが、TVF アプローチよりも適切に大規模データセットに対応できます。

  • 整合性。データベースは、暗号化されたデータを含む列に対して、一意のインデックスまたは外部キーなどの整合性の制約を適用することはできません。

アプリケーション エラー管理

前述した 2 つの対策は、データベース レイヤでの問題のみに対処しようとしていました。3 つ目のオプションは、アプリケーション全体の他の部分で問題に対処します。このアプローチでは、秘密のチャネルまたは推測のチャネルを構成するエラーが発生する、という小さなリスクを容認しています。ただし、間にあるソリューションのレイヤで (オブジェクト指向データ アクセス レイヤなど)、エラー トラップとログを慎重に実装します。データベースの特定のエラーをトラップし、一般的なエラー メッセージのみをユーザーに表示することで、推測に利用される詳細な情報は減ります。また、エラーをログすることで、システムを悪用しようとしたことを示す疑わしいパターンについて監視できる、監査記録になります。

対策のまとめ

対策としてどのアプローチを利用するかについては、状況に応じて判断する必要があります。あるアプリケーションで想定される脅威において、悪用の影響がそれほど重要ではなく、複雑さを容認したりパフォーマンスを犠牲にしたりする価値がない場合もあります。ただし、必要であれば、このようなアプローチを利用して脆弱性をなくすこともできます。

まとめ

ここで提示した設計を SQL Server 2005 データベースに使用すると、任意のセキュリティ ラベル スキームに基づいて、行レベル セキュリティとセル レベル セキュリティをサポートできます。ビューや SQL Server データの暗号など、SQL Server が備える構造を使用して、データベース内の行およびセルに対するアクセス制限を強制適用することができます。ユーザー アクセスの評価は、固有の SQL Server セキュリティ モデルに基づきます。このアセンブリは徹底的にテストされた安全な要素で構成されているため、特別なカスタム コードを使用して主要な承認処理とフィルタ処理の手順を実行するアプローチよりも優れています。

セキュリティ ラベルに基づいてセル レベルでアクセスを制御する機能は、リレーショナル エンジン内部に備わっており、独自の機能です。これにより、多くの分野で秘密情報の管理を大幅に改善し、また操作の要件に合わせてデータを高速かつ柔軟に活用する機能を強化できる可能性があります。

付録 1 : instead-of トリガの例

以下のコードでは、ロジック例についてのみ示します。例をわかりやすくするために、意図的に 1 行の操作でのみ機能するようにしました。

CREATE TRIGGER dbo.IO_Insert_titles ON titles
INSTEAD OF INSERT
AS
DECLARE @RLSMappingID int
DECLARE @KeyMappingID int
DECLARE @KeyName nvarchar(256)
DECLARE @CertName nvarchar(256)
DECLARE @KeyGUID uniqueidentifier
DECLARE @KeyAlreadyOpen bit
DECLARE @CallerName sysname

BEGIN TRY
--Bail if @@ROWCOUNT > 1 (temporary)
IF @@ROWCOUNT > 1
RAISERROR('Only one row at a time for now!', 12, 1)

--Ensure row_label and advance_label are NOT NULL
DECLARE @RowClassification SecurityLabel
DECLARE @AdvanceClassification SecurityLabel
SELECT @RowClassification = row_label, 
@AdvanceClassification = advance_label 
FROM inserted
IF @RowClassification IS NULL
RAISERROR('row_label is required', 12, 1)
IF @AdvanceClassification IS NULL
RAISERROR('advance_label is required', 12, 1)

--Check write-down logic
EXECUTE AS CALLER
SELECT @CallerName = CURRENT_USER
REVERT
DECLARE @UserClearance SecurityLabel
SET @UserClearance = dbo.GetUserLabel(@CallerName)
IF @UserClearance IS NULL
RAISERROR('user rights not sufficient to write this data', 12, 1)
IF dbo.Dominates(@UserClearance, @RowClassification) = 0
RAISERROR('user rights not sufficient to write this data', 12, 1)
IF dbo.Dominates(@UserClearance, @AdvanceClassification) = 0
RAISERROR('user rights not sufficient to write this data', 12, 1)

--Get RLSMappingID for row_label
exec usp_GetRLSMappingID @RowClassification, @RLSMappingID OUTPUT

--Get KeyName and CertName for advance_label
exec usp_GetLabelDetails @AdvanceClassification, @KeyMappingID OUTPUT, 
@KeyName OUTPUT, @CertName OUTPUT, @KeyGUID OUTPUT
exec usp_IsKeyOpen @KeyName, @KeyAlreadyOpen
EXEC('open symmetric key ' + @KeyName + ' using certificate ' + @CertName)

--Do insert, including rls_mapping_id and encryption of advance
INSERT INTO tblTitles
(title_id, title, type, pub_id, price, advance, advance_encrypted, 
advance_label, royalty, ytd_sales, notes, pubdate, rls_mapping_id)
SELECT title_id, 
title, 
type, 
pub_id, 
price, 
EncryptByKey(@KeyGUID, CONVERT(varchar(50), advance)), 
1, 
advance_label, 
royalty, 
ytd_sales, 
notes, 
pubdate, 
@RLSMappingID
FROM inserted

END TRY
BEGIN CATCH
    DECLARE @ErrorMessage NVARCHAR(400);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;
    SELECT @ErrorMessage = ERROR_MESSAGE();
    SELECT @ErrorSeverity = ERROR_SEVERITY();
    SELECT @ErrorState = ERROR_STATE();

IF @@TRANCOUNT > 0
ROLLBACK
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

--Whether successful or not, ensure any key opened by this routine is closed
BEGIN TRY
IF @KeyName IS NOT NULL AND @KeyAlreadyOpen = 0
EXEC('close symmetric key ' + @KeyName)
END TRY
BEGIN CATCH
--suppress error
END CATCH

GO

付録 2 : 対称キーの暗号化オプション

SQL Server 2005 では、複数の対称キー暗号化アルゴリズムをサポートしています。サポート対象のアルゴリズムとそのキー サイズを以下の表に示します。

アルゴリズム

キー サイズ

AES_256 (Rijndael 256)

256

AES_192 (Rijndael 192)

192

AES_128 (Rijndael 128)

128

DES

64

TRIPLE_DES

128

RC2

128

RC4

40

DESX

192

使用する暗号化アルゴリズムの選択は、対称キーを作成するときに SQL DDL で容易に制御できます。これらのアルゴリズムはいずれも、このドキュメントで説明した設計で使用できます。


© 2009 Microsoft Corporation. All rights reserved. 使用条件 | 商標 | プライバシー
Page view tracker