|
|
特集:.NET 4の新セキュリティ・モデル解説
CASが廃止。.NET 4のセキュリティはどうなるのか?
デジタルアドバンテージ 一色 政彦
2010/01/26 |
|
|
■新しいセキュリティ・モデルの動作を確認・検証する
●アプリケーション・ドメインをサンドボックス化する例
ここでは、独自のサンドボックスを作成して完全信頼や部分信頼の状況を作り出し、独自に指定したアクセス許可セットの中でコードの実行がどうなるかを確かめてみる。
なお、このようにサンドボックス化されたアプリケーション・ドメインを作成する利点は、部分信頼のセキュリティ環境で実行されたときの動作をテストできることだ。以下の内容は、そのようなときにも活用できるだろう。
以下で作成するサンプル・アプリケーション(Visual Studio 2010)は下記のリンクからダウンロードできる。
今回のサンプル・アプリケーションでは、下記の3つのプロジェクトを作成している。
- 0_SecureSandboxプロジェクト:サンドボックス化された部分信頼のアプリケーション・ドメインと完全信頼のアセンブリ
- 1_UntrustedCodeプロジェクト:部分信頼のアセンブリ
- 2_Loggingプロジェクト:完全信頼のアセンブリ
|
ソリューション・エクスプローラの内容(VBの例) |
●.NET 4におけるサンドボックス化API
まずサンドボックスを作る方法を説明する。
これまでの.NETでは、サンドボックス化されたアプリケーション・ドメインを作成するための、さまざまなAPIが提供されていた。.NET 4では、これが1つのAPIに一本化された。このAPIは、もともと.NET 2.0で導入されたもので、非常にシンプルにアクセス許可セットや完全信頼にするアセンブリを指定できる。具体的には、下記のシグネチャを持つメソッドである。
AppDomain.CreateDomainメソッド
- 第1パラメータ(String):識別用のアプリケーション・ドメインの名前。
- 第2パラメータ(Evidence):エビデンス(=出自情報)は使われていないので、基本的にnull/Nothingを指定すればよい。
- 第3パラメータ(AppDomainSetup):アプリケーション・ドメインの初期化情報。
- 第4パラメータ(PermissionSet):アクセス許可セット。
- 第5パラメータ(StrongName[]):完全信頼として扱いたいアセンブリの厳密名の配列。
- 戻り値:新しく作成されたアプリケーション・ドメイン。
次のコードは、このメソッド用いてアプリケーション・ドメインを作成するコンソール・アプリケーションのサンプル・コードである。
using System;
using System.IO;
using System.Runtime.Remoting;
using System.Security;
using System.Security.Policy;
using Logging;
// エントリーポイントとなるクラス
internal class Program
{
// プログラムのエントリーポイント
static void Main()
{
// アプリケーション・ドメインの初期化設定
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = Path.GetFullPath(".");
// Internetゾーン・エビデンス(=出自情報)が持つ
// 標準的なアクセス許可セットの取得
Evidence zoneEvidence = new Evidence();
zoneEvidence.AddHostEvidence(new Zone(SecurityZone.Internet));
PermissionSet permissions =
SecurityManager.GetStandardSandbox(zoneEvidence);
// 以下のように独自のアクセス許可セット
// (例:実行と分離ストレージの権限)を作成することも可能。
//PermissionSet permissions =
// new PermissionSet(PermissionState.None);
//permissions.AddPermission(
// new SecurityPermission(SecurityPermissionFlag.Execution));
//permissions.AddPermission(
// new IsolatedStorageFilePermission(
// PermissionState.Unrestricted));
// ホスティング・クラス(Sandboxer)が所属するアセンブリの
// 厳密名を取得(事前にアセンブリをデジタル証明書 で
// 署名しておく必要がある)
StrongName sandboxStrongName = typeof(Sandboxer).
Assembly.Evidence.GetHostEvidence<StrongName>();
// 完全信頼のテスト用クラス(Logger)が所属するアセンブリの
// 厳密名を取得(事前にアセンブリをデジタル証明書で
// 署名しておく必要がある)
StrongName loggerStrongName = typeof(Logger).
Assembly.Evidence.GetHostEvidence<StrongName>();
// サンドボックス化されたアプリケーション・ドメインを作成
// ・第1パラメータ:アプリケーション・ドメイン名
// ・第2パラメータ:エビデンス(※使われないのでnullでよい)
// ・第3パラメータ:初期化設定
// ・第4パラメータ:アクセス許可設定
// ・第5パラメータ:完全信頼にするアセンブリ名
AppDomain newDomain = AppDomain.CreateDomain(
"SandboxedAppDomain",
null,
setup,
permissions,
sandboxStrongName, loggerStrongName);
// ※ホスティング・クラス(この例ではSandboxerクラス)は、
// 動的にコードを実行できるようにするため、完全信頼に指定する
// アプリケーション・ドメインから型をインスタンス化して、
// そのハンドルを取得
// ・第1パラメータ:アプリケーション・ドメイン
// ・第2パラメータ:型が所属するアセンブリ・ファイル名
// ・第3パラメータ:型の名前
ObjectHandle handle = Activator.CreateInstanceFrom(
newDomain,
typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
typeof(Sandboxer).FullName);
// オブジェクト・ハンドルをSandboxerオブジェクトに
// ラップ解除する
Sandboxer newDomainInstance = (Sandboxer)handle.Unwrap();
// ホスティング・クラスのエントリーポイントを呼び出す
newDomainInstance.Main();
}
}
|
Imports System
Imports System.IO
Imports System.Runtime.Remoting
Imports System.Security
Imports System.Security.Policy
Imports Logging
' エントリーポイントとなるクラス
Public Class Program
' プログラムのエントリーポイント。
Public Shared Sub Main()
' アプリケーション・ドメインの初期化設定
Dim setup As New AppDomainSetup()
setup.ApplicationBase = Path.GetFullPath(".")
' Internetゾーン・エビデンス(=出自情報)が持つ
' 標準的なアクセス許可セットの取得
Dim zoneEvidence As New Evidence()
zoneEvidence.AddHostEvidence(New Zone(SecurityZone.Internet))
Dim permissions As PermissionSet =
SecurityManager.GetStandardSandbox(zoneEvidence)
' 以下のように独自のアクセス許可セット
' (例:実行と分離ストレージの権限)を作成することも可能。
'Dim permissions As New PermissionSet(PermissionState.None)
'permissions.AddPermission(
' New SecurityPermission(SecurityPermissionFlag.Execution))
'permissions.AddPermission(
' New IsolatedStorageFilePermission(
' PermissionState.Unrestricted))
' ホスティング・クラス(Sandboxer)が所属するアセンブリの
' 厳密名を取得(事前にアセンブリをデジタル証明書で
' 署名しておく必要がある)
Dim sandboxStrongName As StrongName = GetType(Sandboxer).
Assembly.Evidence.GetHostEvidence(Of StrongName)()
' 完全信頼のテスト用クラス(Logger)が所属するアセンブリの
' 厳密名を取得(事前にアセンブリをデジタル証明書で
' 署名しておく必要がある)
Dim loggerStrongName As StrongName = GetType(Logger).
Assembly.Evidence.GetHostEvidence(Of StrongName)()
' サンドボックス化されたアプリケーション・ドメインを作成
' ・第1パラメータ:アプリケーション・ドメイン名
' ・第2パラメータ:エビデンス(※使われないのでNothingでよい)
' ・第3パラメータ:初期化設定
' ・第4パラメータ:アクセス許可設定
' ・第5パラメータ:完全信頼にするアセンブリ名
Dim newDomain As AppDomain = AppDomain.CreateDomain(
"SandboxedAppDomain",
Nothing,
setup,
permissions,
sandboxStrongName, loggerStrongName)
' ※ホスティング・クラス(この例ではSandboxerクラス)は、
' 動的にコードを実行できるようにするため、完全信頼に指定する
' アプリケーション・ドメインから型をインスタンス化して、
' そのハンドルを取得
' ・第1パラメータ:アプリケーション・ドメイン
' ・第2パラメータ:型が所属するアセンブリ・ファイル名
' ・第3パラメータ:型の名前
Dim handle As ObjectHandle = Activator.CreateInstanceFrom(
newDomain,
GetType(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
GetType(Sandboxer).FullName)
' オブジェクト・ハンドルをSandboxerオブジェクトに
' ラップ解除する
Dim newDomainInstance As Sandboxer = handle.Unwrap()
' ホスティング・クラスのエントリーポイントを呼び出す
newDomainInstance.Main()
End Sub
End Class |
|
サンドボックス化されたアプリケーション・ドメインを作成するサンプル・コード(上:C#、下:VB) |
上記のコードの意味は、コード中のコメントを参考にしていただきたい。
注意点をいくつか挙げておくと、この例では、Internetゾーン・エビデンスに対して標準的なアクセス許可セット(以降、「ゾーン・パーミッション」と略す)を取得しているが、SecurityZone列挙体の別の値を指定することで、さまざまなゾーン・パーミッションを取得できる(SecurityZone列挙体の各値に対応するアクセス許可セットの内容は、前掲の「各ゾーン標準のアクセス許可の一覧表」を参照してほしい)。ただし、NoZoneとUntrustedのゾーン・パーミッションを取得しても、Executionフラグを持つSecurityPermissionアクセス許可が含まれていないので、アセンブリの実行に失敗する。
MyComputerゾーン・パーミッションを取得した場合、アプリケーション・ドメインが完全信頼になり、その中のすべてのアセンブリも完全信頼となる。それ以外のゾーン・パーミッションを取得した場合は、アプリケーション・ドメインが部分信頼になり、その中のすべてのアセンブリも部分信頼となる。上記の例では、Internetゾーン・パーミッションを取得しているので、アプリケーション・ドメインは部分信頼になる。
テストなどの目的で一部のアセンブリを完全信頼としたい場合には、AppDomain.CreateDomainメソッドの第5パラメータにそのアセンブリ群の厳密名を指定すればよい。なお、厳密名を取得するにはアセンブリの署名が必要になる。上記の例では、SandboxerクラスとLoggerクラスを完全信頼にしているが、その理由は下記のとおりだ。
- Sandboxerクラス:クラス内部のメソッドでCriticalコードを呼び出すため。
- Loggerクラス:Transparentコードから完全信頼のコード(=Criticalコード)の呼び出しを検証するため。
なお、完全信頼にしたアセンブリ内のコードは、すべてCriticalコードと見なされる。また、部分信頼にしたアセンブリ内のデフォルトのコードは、自動的にTransparentコードと見なされる。ただし、明示的にSecuritySafeCritical属性/SecurityCritical属性を指定しているコードは、この限りではない。
●Criticalコード(完全信頼)からTransparentコード(部分信頼)を呼び出す
上記のコードでは、サンドボックス化されたアプリケーション・ドメインを作成した後、その中にあるSandboxerクラスをインスタンス化して、Mainメソッドを呼び出している。Mainメソッドのコードは次のとおりだ。
using System;
using System.Reflection;
// サンドボックス化されたホスティング・クラス(=ホスト環境の
// メイン・クラス)。MarshalByRefObjectクラスを継承することで、
// 外部のアプリケーション・ドメイン(この例ではProgramクラスの
// アプリケーション・ドメイン)から(境界を越えて)
// 呼び出せるようになる
public class Sandboxer : MarshalByRefObject
{
// サンドボックス化されたホスト環境のエントリーポイント
public void Main()
{
// 外部アセンブリの部分信頼のコードを実行する
string assemblyName = "UntrustedCode";
string typeName = "UntrustedCode.UntrustedClass";
string methodName = "WriteToIsolatedStorage";
Object[] parameters = { 20100126 };
try
{
MethodInfo target = Assembly.Load(assemblyName).
GetType(typeName).GetMethod(methodName);
// テスト目的で透過性レベルを調べてみる
Console.Write(methodName + "メソッドの透過性レベル:");
if (target.IsSecurityTransparent)
{
Console.WriteLine("Transparent");
}
else if (target.IsSecuritySafeCritical)
{
Console.WriteLine("SafeCritical");
}
else if (target.IsSecurityCritical)
{
Console.WriteLine("Critical");
}
else
{
Console.WriteLine("不明");
}
bool retVal = (bool)target.Invoke(null, parameters);
}
catch (Exception ex)
{
Console.WriteLine(
"セキュリティ例外を検出:\n{0}", ex.ToString());
Console.ReadLine();
throw;
}
// 実行内容を確認するために実行を停止
Console.ReadKey();
}
} |
Imports System
Imports System.Reflection
' サンドボックス化されたホスティング・クラス(=ホスト環境の
' メイン・クラス)。MarshalByRefObjectクラスを継承することで、
' 外部のアプリケーション・ドメイン(この例ではProgramクラスの
' アプリケーション・ドメイン)から(境界を越えて)
' 呼び出せるようになる。
Public Class Sandboxer
Inherits MarshalByRefObject
' サンドボックス化されたホスト環境のエントリーポイント
Public Sub Main()
' 外部アセンブリの部分信頼のコードを実行する
Dim assemblyName = "UntrustedCode"
Dim typeName = "UntrustedCode.UntrustedClass"
Dim methodName = "WriteToIsolatedStorage"
Dim parameters As Object() = {20100126}
Try
Dim target As MethodInfo = Assembly.Load(assemblyName).
GetType(typeName).GetMethod(methodName)
' テスト目的で透過性レベルを調べてみる
Console.Write(methodName + "メソッドの透過性レベル:")
If target.IsSecurityTransparent Then
Console.WriteLine("Transparent")
ElseIf target.IsSecuritySafeCritical Then
Console.WriteLine("SafeCritical")
ElseIf target.IsSecurityCritical Then
Console.WriteLine("Critical")
Else
Console.WriteLine("不明")
End If
Dim retVal As Boolean = target.Invoke(Nothing, parameters)
Catch ex As Exception
Console.WriteLine(
"セキュリティ例外を検出:\n{0}", ex.ToString())
Console.ReadLine()
Throw
End Try
' 実行内容を確認するために実行を停止
Console.ReadKey()
End Sub
End Class |
|
完全信頼:SandboxerクラスのMainメソッドのコード(上:C#、下:VB) |
このコードでは、Assembly.Loadメソッドを使って動的に外部のUntrustedCodeアセンブリをロードして、UntrustedCode.UntrustedClassクラスに所属するWriteToIsolatedStorageメソッドを呼び出している。アプリケーション・ドメインが部分信頼なので、このアセンブリは部分信頼としてロードされる点に注意してほしい。
●アクセス許可が必要なAPIを呼び出す
次にそのWriteToIsolatedStorageメソッドのコードを見てみよう。
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Reflection;
using Logging;
namespace UntrustedCode
{
// 部分信頼のクラス(権限が必要)
public class UntrustedClass
{
// 分離ストレージへの書き込みとログ出力(アクセス許可が必要)
// ・第1パラメータ:保存するデータ
// ・戻り値:成功(true)、失敗(false)
public static bool WriteToIsolatedStorage(int saveData)
{
// テスト目的で透過性レベルを調べてみる
MethodInfo target = typeof(Logger).GetMethod("Log");
Console.Write("Logメソッドの透過性レベル:");
if (target.IsSecurityTransparent)
{
Console.WriteLine("Transparent");
}
else if (target.IsSecuritySafeCritical)
{
Console.WriteLine("SafeCritical");
}
else if (target.IsSecurityCritical)
{
Console.WriteLine("Critical");
}
else
{
Console.WriteLine("不明");
}
// AppDomain.IsFullyTrustedプロパティの使用例
Console.Write("現在のアプリケーション・ドメイン:");
Console.WriteLine(
AppDomain.CurrentDomain.IsFullyTrusted ?
"完全信頼" : "部分信頼");
Console.Write("実行中のアセンブリ:");
Console.WriteLine(
Assembly.GetExecutingAssembly().IsFullyTrusted ?
"完全信頼" : "部分信頼");
Console.Write("mscorlibアセンブリ:");
Console.WriteLine(
typeof(int).Assembly.IsFullyTrusted ?
"完全信頼" : "部分信頼");
// 分離ストレージへの書き込み(アクセス許可が必要)
IsolatedStorageFile isoStore =
IsolatedStorageFile.GetUserStoreForDomain();
IsolatedStorageFileStream isoStream =
new IsolatedStorageFileStream(
"FileName.txt",
FileMode.OpenOrCreate,
FileAccess.Write,
isoStore);
using (StreamWriter writer = new StreamWriter(isoStream))
{
writer.Write("データ「");
writer.Write(saveData.ToString());
writer.WriteLine("」を書き込みました!");
}
isoStream.Close();
isoStore.Dispose();
// (完全信頼に指定された)Loggerクラスのアセンブリを使って
// ログ出力を行う。(※本アセンブリの部分信頼のコードから、
// 完全信頼のコードを呼び出すには、
// 完全信頼の側のAssemblyInfo.csファイルで
// [assembly: AllowPartiallyTrustedCallers()]を記述する。)
Logger.Log("分離ストレージへの書き込み:" + saveData);
return true;
}
}
}
|
Imports System
Imports System.IO
Imports System.IO.IsolatedStorage
Imports System.Security.Permissions
Imports Logging
Imports System.Security
Imports System.Reflection
' 部分信頼のクラス(権限が必要)
Public Class UntrustedClass
' 分離ストレージへの書き込みとログ出力(アクセス許可が必要)
' ・第1パラメータ:保存するデータ
' ・戻り値:成功(true)、失敗(false)
Public Shared Function WriteToIsolatedStorage(ByVal saveData As Integer) As Integer
' テスト目的で透過性レベルを調べてみる
Dim target As MethodInfo = GetType(Logger).GetMethod("Log")
Console.Write("Logメソッドの透過性レベル:")
If target.IsSecurityTransparent Then
Console.WriteLine("Transparent")
ElseIf target.IsSecuritySafeCritical Then
Console.WriteLine("SafeCritical")
ElseIf target.IsSecurityCritical Then
Console.WriteLine("Critical")
Else
Console.WriteLine("不明")
End If
' AppDomain.IsFullyTrustedプロパティの使用例
Console.Write("現在のアプリケーション・ドメイン:")
Console.WriteLine(
IIf(AppDomain.CurrentDomain.IsFullyTrusted,
"完全信頼", "部分信頼"))
Console.Write("実行中のアセンブリ:")
Console.WriteLine(
IIf(Assembly.GetExecutingAssembly().IsFullyTrusted,
"完全信頼", "部分信頼"))
Console.Write("mscorlibアセンブリ:")
Console.WriteLine(
IIf(GetType(Integer).Assembly.IsFullyTrusted,
"完全信頼", "部分信頼"))
' 分離ストレージへの書き込み(アクセス許可が必要)
Dim isoStore As IsolatedStorageFile =
IsolatedStorageFile.GetUserStoreForDomain()
Dim isoStream As New IsolatedStorageFileStream(
"FileName.txt",
FileMode.OpenOrCreate,
FileAccess.Write,
isoStore)
Using writer As New StreamWriter(isoStream)
writer.Write("データ「")
writer.Write(saveData.ToString())
writer.WriteLine("」を書き込みました!")
End Using
isoStream.Close()
isoStore.Dispose()
' (完全信頼に指定された)Loggerクラスのアセンブリを使って
' ログ出力を行う。(※本アセンブリの部分信頼のコードから、
' 完全信頼のコードを呼び出すには、
' 完全信頼の側のAssemblyInfo.csファイルで
' [assembly: AllowPartiallyTrustedCallers()]を記述する。)
Logger.Log("分離ストレージへの書き込み:" & saveData)
Return True
End Function
End Class |
|
部分信頼:UntrustedClassクラスのWriteToIsolatedStorageメソッドのコード(上:C#、下:VB) |
WriteToIsolatedStorageメソッドでは、IsolatedStorageFilePermissionアクセス許可が必要な分離ストレージへの書き込みを行った後、アプリケーション・ドメイン作成時に完全信頼に指定したアセンブリ(※参照設定済み)のLoggerクラスのLogメソッドを呼び出している。
さらにWriteToIsolatedStorageメソッドでは、.NET 4のセキュリティ内容が理解できるさまざまな検証処理も追記した。ここで、.NET 4のセキュリティ内容を調査できるプロパティ群について簡単に紹介しておこう。
●.NET 4のセキュリティ内容を診断する
まず、アプリケーション・ドメインと、その内部のアセンブリ群のセキュリティ環境(完全信頼/部分信頼)を調べるためのプロパティが、下記のように用意されている。
- AppDomain.IsFullyTrustedプロパティ
- Assembly.IsFullyTrustedプロパティ
次に、透過性レベル(Transparent/SafeCritical/Critical)を調べるためのプロパティが、下記のようなプロパティがある。
- MethodBase.IsSecurityTransparentプロパティ
- MethodBase.IsSecuritySafeCriticalプロパティ
- MethodBase.IsSecurityCriticalプロパティ
さらに、アプリケーション・ドメインが持つアクセス許可セット(=PermissionSetオブジェクト)を取得するためのプロパティも用意されている。ただし、Criticalコードなので完全信頼のコードから呼び出す必要がある。部分信頼のこのメソッド内では呼び出せない。
- AppDomain.PermissionSetプロパティ
- Assembly.PermissionSetプロパティ
なお、今回のサンプル・アプリケーションを実行すると、上記のセキュリティ内容調査コードは次のように出力される。
WriteToIsolatedStorageメソッドの透過性レベル:Transparent
Logメソッドの透過性レベル:SafeCritical
現在のアプリケーション・ドメイン:部分信頼
実行中のアセンブリ:部分信頼
mscorlibアセンブリ:完全信頼 |
|
セキュリティ内容調査コードの出力例 |
ここでLogger.Logメソッドが(Criticalではなく)SafeCriticalなっている理由は後述する。
●Transparentコード(部分信頼)からCriticalコード(完全信頼)を呼び出す
続いて、完全信頼のアセンブリ内のLogger.Logメソッドのコードを見てみよう。
using System;
using System.Security;
namespace Logging
{
// ログ出力の機能を提供するクラス
public class Logger
{
// ログ出力を行う
//[SecurityCritical]
[SecuritySafeCritical]
public static void Log(string text)
{
Console.Write("ログ出力:");
Console.WriteLine(text);
}
}
} |
Imports System.Security
' ログ出力の機能を提供するクラス
Public Class Logger
' ログ出力を行う
'<SecurityCritical()>
<SecuritySafeCritical()>
Public Shared Sub Log(ByVal text As String)
Console.Write("ログ出力:")
Console.WriteLine(text)
End Sub
End Class |
|
完全信頼:LoggerクラスのLogメソッドのコード(上:C#、下:VB) |
このコードで特に難しいところはないと思うが、本稿の中で紹介したSecurityCritical/SecuritySafeCritical属性を参考までにメソッドに適用してみた。先ほどの出力結果で、Logメソッドの透過性レベルが「SafeCritical」になっていたのはこのためである。
以上でコードはすべてそろった。この状態でソリューション全体をビルドしてデバッグ実行してみよう。すると、次の画面のようにTargetInvocationException例外が発生する。
|
部分信頼のコードから完全信頼のコードを呼び出したときのエラー |
これは前述したセキュリティ仕様のとおり、部分信頼のTransparentコードから、完全信頼のコード(自動的にCriticalコードになる)を呼び出したために発生している。
この例外を発生させないための設定が用意されている。具体的には、完全信頼になっているアセンブリのAssemblyInfo.cs/AssemblyInfo.vbファイルに、次のコードを記述すればよい。
[assembly: AllowPartiallyTrustedCallers()] |
<Assembly: AllowPartiallyTrustedCallers()> |
|
部分信頼のTransparentコードから、完全信頼のCriticalコードを呼び出し可能にする設定(上:C#、下:VB) |
この設定により、完全信頼のアセンブリは、部分信頼からのアクセスを受け付けるようになり、さらにアセンブリ内のすべてのコードをTransparentとして取り扱うようになる。ただし、この設定がセキュリティ脆弱性につながる可能性もあるので取り扱いには注意してほしい。
■
以上、.NET 4の新セキュリティ・モデルを一通り紹介した。セキュリティの動作などで気になる部分がある場合には、まずは本稿のサンプル・アプリケーションをいろいろとカスタマイズして検証してみてほしい。
筆者による.NET/Azure関連の情報共有について
はてなブックマークやTwitterで、主に.NET/Windows Azure開発関連の情報共有を行っています。はてなブックマークでは、主に英語のブログへのリンク共有を行っています。Twitterでは、リンク共有に加え、簡単な投稿も行っています。どちらもフォローしたり、RSSフィードを購読したりできます。
|
INDEX |
|
特集:.NET 4の新セキュリティ・モデル解説 |
|
CASが廃止。.NET 4のセキュリティはどうなるのか? |
|
1.新しいセキュリティ・モデルを理解する |
|
2.新しいセキュリティ・モデルの動作を確認・検証する |
|