解説

インサイド .NET Framework [改訂版]

第10回 ロールベース・セキュリティ

吉松 史彰
2003/09/10
Page1 Page2 Page3 Page4

プログラムで明示的にアクセス制御を行う

 ロールベース・セキュリティを適用する方法は2つある。1つはプログラムのソース・コードとして明示的にメソッド呼び出しを記述する(命令的;imperative)方法と、カスタム属性を使って宣言する(宣言的;declarative)方法の2つだ。まずはプログラムで記述する方法を解説しよう。

 次のコードでは、このプログラムをWindows上で実行しているユーザーが、コマンドライン・オプションに指定された名前のWindowsグループに属しているかどうかを調べている。

using System;
using System.Threading;
using System.Security.Principal;

class App {
  static void Main(string[] args) {
    AppDomain.CurrentDomain.SetPrincipalPolicy(
                PrincipalPolicy.WindowsPrincipal);

    IPrincipal principal = Thread.CurrentPrincipal;
    IIdentity identity = principal.Identity;

    Console.WriteLine("{0} は {1} ロールに所属していま{2}。",
            identity.Name,
            args[0],
            (principal.IsInRole(args[0]) ? "す" : "せん"));
  }
}
明示的なメソッド呼び出しによるロールベース・セキュリティの適用例
このプログラムでは、プログラムを実行したユーザーが、コマンドライン・オプションに指定された名前のWindowsグループに属しているかどうかを調べることができる。

 Mainメソッドの1行目で、AppDomainクラスのSetPrincipalPolicyメソッドを呼び出していることに注意してほしい。これを行わないと、Windows上で稼働しているアプリケーションであっても、デフォルトではWindowsPrincipalオブジェクトが作成されない。また、現在プログラムを実行しているプリンシパルを取得するために、ThreadクラスのCurrentPrincipalプロパティにアクセスしている点にも注意してほしい。なお、SetPrincipalPolicyはThread.CurrentPrincipalを呼び出す前に呼び出しておかなければならない。

 このプログラムを実行すると、結果は次のようになる。

% App.exe "COMPUTER\Debugger Users"
COMPUTER\User1 は COMPUTER\Debugger users ロールに所属しています。
上記プログラムの実行結果例(1)
コンピュータ名「COMPUTER」でこのプログラムを実行したユーザー「User1」は、グループ「Debugger Users」に属していることが分かる。

 ただし、コマンドライン・オプションにはWindowsの組み込みグループを指定することはできない。上記のプログラムを次のように実行すると、一見おかしな結果になる。

% App.exe "COMPUTER\Users"
COMPUTER\User1 は COMPUTER\Users ロールに所属していません。
上記プログラムの実行結果例(2)
ユーザー「User1」は実際には、Windowsの組み込みグループである「Users」に属しているが、これをチェックするにはWindowsBuiltInRole列挙体を引数にとるIsInRoleメソッドを利用する必要がある。

 これは、WindowsPrincipalクラスのIsInRoleメソッドのうち、文字列を引数に取るバージョン(上記のプログラムで利用している)では、組み込みグループのチェックができないからだ。組み込みグループをチェックする場合は、WindowsPrincipalクラスに固有のIsInRoleメソッドの実装である、System.Security.Principal.WindowsBuiltInRole列挙体を取るバージョンを呼び出さなければならない。WindowsBuiltInRole列挙には次の値が定義されている。Windowsの管理業務を行った経験があれば、見慣れた名前だろう。もっとも、ローカル・グループ名から複数形の「s」が抜けていることに注意しなければならない。

  • AccountOperator
  • Administrator
  • BackupOperator
  • Guest
  • PowerUser
  • PrintOperator
  • Replicator
  • SystemOperator
  • User

 WindowsBuiltInRole列挙をとるバージョンのメソッドはWindowsPrincipalクラスに固有なので、呼び出すときには次のようにIPrincipalではなくWindowsPrincipalを使わなければならない。

((WindowsPrincipal)principal).IsInRole(WindowsBuiltInRole.Administrator);

宣言的アクセス制御

 プログラムで明示的にIsInRoleメソッドを呼び出すと、例えばメソッドの途中でif文などを使って、ロールごとに処理を分岐させることができる。購買申請の承認において、「承認」メソッドで金額をチェックし、10万円以上の場合はIsInRole("Managers")がtrueを返さなければならない、などといった使い方に応用できるだろう。

 しかし、そもそもあるロールに属していなければ呼び出してはならないメソッドの場合は、プログラムで明示的にIsInRoleを呼び出すとメソッド全体をifで囲まなければならなくなる。あるいは、呼び出す側で先にそのチェックを行えばいいと思われるかもしれないが、この方法ではアクセス制御機構が複数の個所に分かれて記述されることになり、記述漏れの可能性が高くなる。そこで.NET Frameworkでは、カスタム属性という伝家の宝刀を使って、ロールベース・セキュリティをより簡単に適用できるようにしている。

 ロールベース・セキュリティをカスタム属性を使って適用するには、System.Security.Permissions.PrincipalPermissionAttribute属性を使う。この属性の使い方は、すでに連載で解説したコード・アクセス・セキュリティにおける属性ベースの設定と同じだ。例えば、Approveというメソッドに適用する場合は、C#では次のように記述する。

[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Managers")]
bool Approve(decimal amount) { …… }

 コード・アクセス・セキュリティの解説では、System.Security.Permissions.SecurityAction列挙の値としてDemand以外にも幾つか紹介したが、PrincipalPermissonAttributeで設定できるのはそのうち、Demand、InheritanceDemand、LinkDemandの3つだけである。また、属性の“Role=”の部分には、所属をチェックしたいロールの名前を指定する。

 例えば次のコードでは、Windowsに組み込みのAdministratorsグループのメンバだけがFormatDiskメソッドを呼び出せる。Administratorsグループのメンバでないユーザーが呼び出そうとすると、コード・アクセス・セキュリティのときと同様に、System.Security.SecurityExceptionが発生する。属性に記述するときの、組み込みグループの名前は「BuitIn\グループ名」となることに注意してほしい。通常のグループは「コンピュータ名またはドメイン名\グループ名」で指定できる。

using System;
using System.Threading;
using System.Security.Principal;
using System.Security.Permissions;

class App {
  static void Main(string[] args) {
    AppDomain.CurrentDomain.SetPrincipalPolicy(
                PrincipalPolicy.WindowsPrincipal);
    FormatDisk('C');
  }

  [PrincipalPermission(
      SecurityAction.Demand, Role="BuiltIn\\Administrators")]
  static void FormatDisk(char driveLetter) {
    SHFormatDrive(IntPtr.Zero,
      (uint)(char.ToLower(driveLetter) - 'a'), 0, 1);
  }

  static int SHFormatDrive(IntPtr hwnd, uint drive, uint fmtID, uint options) { return 0; }*1
}
属性によりロールベース・セキュリティの適用例
このプログラムでは、Windowsに組み込みのAdministratorsグループのメンバがプログラムを実行した場合にのみ、FormatDiskメソッドを呼び出すことができる。
 
*1 ここでは何もしていないが、[DllImport("shell32.dll")]という属性を付けて、extern修飾子を追加すれば、立派にWin32 APIを呼び出してハードディスクをフォーマットできる。

 なお、カスタム属性を使って複数のロールを設定したい場合は、次のように複数の属性を記述すればよい。

  [PrincipalPermission(
      SecurityAction.Demand, Role="BuiltIn\\Power Users")]
  [PrincipalPermission(
      SecurityAction.Demand, Role="BuiltIn\\Administrators")]
  static void FormatDisk(char driveLetter) { …… }

 ちなみに、コード・アクセス・セキュリティのアクセス許可オブジェクトがそうだったように、PrincipalPermissionAttributeにも対応するPrincipalPermissionクラスがある。カスタム属性の宣言と同様の機能は、次のようにコードを記述しても実現できる。

  PrincipalPermission p
    = new PrincipalPermission(null, "BuiltIn\\Administrators");
  p.Demand();

 複数のロールを指定したい場合は、次のようにUnionメソッドでPrincipalPermissionオブジェクトを組み合わせる。

  PrincipalPermission p1
    = new PrincipalPermission(null, "BuiltIn\\Administrators");
  PrincipalPermission p2
    = new PrincipalPermission(null, "BuiltIn\\Power Users");
  System.Security.IPermission p = p1.Union(p2);
  p.Demand();

 なお、PrincipalPermissionクラスのコンストラクタの第1引数はアイデンティティの名前だが、「ロールベース」セキュリティでは通常は使わない。実はPrincipalPermissionAttributeにもNameというプロパティがあって、アイデンティティの名前を指定できるのだが、「ロールベース」セキュリティでは推奨されない。

 

 INDEX
  解説 インサイド .NET Framework [改訂版]
  第10回 ロールベース・セキュリティ
    1.アイデンティティとプリンシパル
  2.ロールベース・セキュリティの適用
    3.Webアプリケーションとロールベース・セキュリティ
    4.WebアプリケーションとNTFSアクセス制御
 
インデックス・ページヘ  「解説:インサイド .NET Framework [改訂版]」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間