目次 | 前の項目 | 次の項目 | Java セキュリティアーキテクチャ |
Permission クラスは、システムリソースへのアクセス権を表します。java.security.Permission クラスは abstract クラスで、このクラスから、特定のアクセス権を表すサブクラスが作られます。
perm = new java.io.FilePermission("/tmp/abc", "read");
新しいアクセス権は、Permission クラス、またはそのサブクラスの 1 つ (java.security.BasicPermission など) からサブクラス化されます。サブクラス化された、BasicPermission 以外のアクセス権は通常、それ自体のパッケージに属します。したがって、FilePermission は java.io パッケージにあります。すべての新規 Permission クラスに実装する必要のある重要な abstract メソッドが、
implies
メソッドです。基本的に、「a implies b」は、アクセス権 a を与えられている者は、当然アクセス権 b を与えられるということを意味します。この点は、アクセス制御の決定の際に重要です。abstract クラス java.security.Permission には、java.security.PermissionCollection という名前の abstract クラスと java.security.Permissions という final クラスが関連します。
java.security.PermissionCollection クラスは、ファイルのアクセス権など 1 つのカテゴリの Permission オブジェクトのコレクション (複製可能なセット) を表し、簡単にグループ化できるようになっています。ファイルのアクセス権などのように、アクセス権を PermissionCollection オブジェクトに任意の順序で追加できる場合は、
implies
メソッドが呼び出されたときに、PermissionCollection オブジェクトが確実に正しいセマンティクスに従うことが重要です。java.security.Permissions クラスは、Permission オブジェクトのコレクションを表します。つまり、異種のアクセス権のスーパーコレクションです。
アプリケーションには、システムがサポートしているアクセス権の新規のカテゴリを自 由に追加できます。アプリケーションに特定のアクセス権を追加する方法については、あとで説明します。
この abstract クラスは、すべてのアクセス権の上位クラスです。すべてのアクセス権に必要な必須機能を定義します。アクセス権の各インスタンスは、通常、コンストラクタに 1 つ以上の文字列パラメータを渡すことによって生成されます。2 つのパラメータを使う一般的なケースでは、通常、1 つ目のパラメータはターゲットの名前 (アクセス権の目的のファイル名など) で、2 つ目のパラメータは動作 (ファイルに対する読み取り動作など) です。通常、複数の動作のセットをコンマで区切った文字列の組み合わせで指定できます。
このクラスは、同一種のアクセス権のコレクションを持ちます。つまり、このクラスの各イン スタンスは、同じタイプのアクセス権だけを持ちます。
このクラスは、異種のアクセス権のコレクションのために設計されています。基本的に、この クラスは java.security.PermissionCollection オブジェクトのコレクションです。
前述したように、セキュリティポリシーの内部状態は通常、各コードソースに関連するアクセ ス権オブジェクトによって表されます。しかし、Java テクノロジの動的な性質を考慮すると、ポリシーが初期化された時点で、特定のアクセス権クラスを実装した実際のコードがまだロードされておらず、Java アプリケーション環境に定義されていない可能性があります。たとえば、参照されたアクセス権クラスは、あとにロードされる JAR ファイル内にある可能性があります。UnresolvedPermission クラスは、こうした「未解決」のアクセス権を保持するために使用されます。同様に、 java.security.UnresolvedPermissionCollection クラスには UnresolvedPermission アクセス権クラスのコレクションが保存されます。
アクセス制御の際に、以前は未解決であったものの、現在そのクラスがロードされてい るアクセス権のタイプがチェックされ、未解決のアクセス権が「解決」されて、適切なアクセス制御が決定されます。つまり、可能であれば、 UnresolvedPermission 内の情報に基づいて適切なクラスタイプの新規オブジェクトのインスタンスが生成されます。この新規オブジェクトが UnresolvedPermission に置き換わり、UnresolvedPermission は削除されます。
このクラスのターゲットは、次のように指定されます。ここで、ディレクトリとファイルの名 前は文字列で、空白文字を含むことはできません。
file
directory (directory/ と同じ)
directory/file
directory/* (このディレクトリ内のすべてのファイル)
* (現在のディレクトリ内のすべてのファイル)
directory/- (このディレクトリ下のファイルシステム内のすべてのファイル)
- (現在のディレクトリ下のファイルシステム内のすべてのファイル)
"<<ALL FILES>>" (ファイルシステム内のすべてのファイル)
「"<<ALL FILES>>"」は特別な文字列で、システム内のすべてのファイルを表します。UNIX システムでは、root ディレクトリの下のすべてのファイルです。MS-DOS システムでは、すべてのドライブのすべてのファイルです。動作は、read、write、delete、 および execute です。ファイルのアクセス権を作成する有効なコード例を次に示します。
import java.io.FilePermission;
FilePermission p = new FilePermission("myfile", "read,write");
FilePermission p = new FilePermission("/home/gong/", "read");
FilePermission p = new FilePermission("/tmp/mytmp", "read,delete");
FilePermission p = new FilePermission("/bin/*", "execute");
FilePermission p = new FilePermission("*", "read");
FilePermission p = new FilePermission("/-", "read,execute");
FilePermission p = new FilePermission("-", "read,execute");
FilePermission p = new FilePermission("<<ALL FILES>>", "read");
このクラスのimplies
メソッドは、ファイルシステムを正確に解釈します。たとえば、FilePermission("/-", "read,execute") は、FilePermission("/home/gong/public_html/index.html", "read") を表し、FilePermission("bin/*", "execute") は FilePermission("bin/emacs19.31", "execute") を表します。注: 多くの場合、これらの文字列の形式はプラットフォームにより異なります。たとえば、Windows システムの C ドライブ上の temp ディレクトリの foo というファイルへの読み取りアクセスを表すには、次の表現を使用します。
FilePermission p = new FilePermission("c:\\temp\\foo", "read");
文字列はトークナイザ (java.io.StreamTokenizer) によって処理されますが、そこでは「\」を使ったエスケープ文字列 (改行を表す「\n」など) が許されるので、単一の「\」記号を表すためには 2 つの連続した「\」記号を使う必要があります。トークナイザが FilePermission のターゲット文字列の処理を終え、2 つの連続した「\」記号を単一の「\」記号に変換すると、結果は次の実際のパスになります
"c:\temp\foo"
全プラットフォーム共通のファイル記述言語ができるまでは、文字列は プラットフォームごとに異なる形式で指定する必要があります。また、「*」や「-」のようなメタシンボルを使用して特定のファイル名を表すことはできませ ん。これは、さしあたり許容できる程度の小さな制限事項だと考えます。UNIX システムでは「/-」と「<<ALL FILES>>」は、どちらもファイルシステム全体を指します(利用可能なファイルシステムであれば、複数のファイルシステムを指すこともで きる)が、別のオペレーティングシステム (Microsoft Windows や MacOS など) ではこの 2 つは、別のターゲットを指す可能性があります。次のように、ターゲットがディレクトリだけで動作が read の場合は、そのディレクトリ内のファイルを一覧表示するためだけのアクセス権が与えられ、ファイルの読み取りは許可されません。
FilePermission p = new FilePermission("/home/gong/", "read");
ファイルの読み取りを許可するには、次のように明示的なファイル名を指定するか、「*」または「-」を指定します。
FilePermission p = new FilePermission("/home/gong/myfile", "read");
FilePermission p = new FilePermission("/home/gong/*", "read");
FilePermission p = new FilePermission("/home/gong/-", "read");
コードは必ず、そのコードと同じ (URL の) 位置、およびそのサブディレクトリの位置からのファイルの読み取り権を自動的に取得します。そのための明示的なアクセス権は必要ありません。
このクラスは、ソケットを通じたネットワークへのアクセス権を表します。このクラスのター ゲットは「hostname:port_range」の形式で指定します。hostname は次の形式で指定します。
hostname (単一ホスト)
IP address (単一ホスト)
localhost (ローカルマシン)
"" ("localhost" と同等)
hostname.domain (ドメイン内の単一ホスト)
hostname.subdomain.domain
*.domain (ドメイン内のすべてのホスト)
*.subdomain.domain
* (すべてのホスト)
つまり、ホストは、DNS 名、数値の IP アドレス、localhost (ローカルマシンの場合)、"" (localhost を指定するのと同じ) のどれかで表現されます。DNS 名によるホスト指定には、ワイルドカード「*」を 1 回使用できます。これを使う場合は、「*.sun.com」のように一番左の位置に使います。
N (単一ポート)
N- (N 以上のすべてのポート)
-N (N 以下のすべてのポート)
N1-N2 (N1 と N2 を含むこの間のすべてのポート)
上の形式で、N、N1、N2 は、0 から 65535 (2^16-1) の範囲内の負でない整数です。ソケットでの動作は、accept、connect、listen、 および resolve です (resolve は基本的に DNS の照合)。resolve 動作は、accept、connect、および listen により暗示されます。つまり、ホストからの入力接続の listen や accept、またはホストへの出力接続の開始が可能なユーザは、そのリモートホストの名前を照合できるようになるということです。
import java.net.SocketPermission;
SocketPermission p = new SocketPermission("java.sun.com","accept");
p = new SocketPermission("204.160.241.99","accept");
p = new SocketPermission("*.com","connect");
p = new SocketPermission("*.sun.com:80","accept");
p = new SocketPermission("*.sun.com:-1023","accept");
p = new SocketPermission("*.sun.com:1024-","connect");
p = new SocketPermission("java.sun.com:8000-9000",
"connect,accept");
p = new SocketPermission("localhost:1024-",
"accept,connect,listen");
SocketPermission("java.sun.com:80,8080","accept") と SocketPermission("java.sun.com,javasun.sun.com","accept") は、有効なソケットアクセス権ではありません。listen 動作はローカルホストのポートだけに適用され、accept 動作はローカルホストとリモートホストの両方のポートに適用されます。両方の動作が必要です。
BasicPermission クラスは Permission クラスを継承します。BasicPermission と同じ命名規約に従おうとするアクセス権クラスの基底クラスとして使用できます。BasicPermission の名前は、指定されたアクセス権の名前です (たとえば exitVM、setFactory、queuePrintJob)。命名規約は、階層的なプロパティ命名規約に従います。名前の末尾の「.」のあとのア スタリスク、または単独のアスタリスクは、ワイルドカードマッチングを指定します。たとえば、「java.*」や「*」は有効ですが、「*java」や 「a*b」は無効です。
動作の文字列 (Permission から継承される) は使用されません。したがって、「名前付き」のアクセス権 (名前を持つが動作リストのないアクセス権。名前付きアクセス権は、ある場合とない場合がある) の基底クラスとして、一般に BasicPermission が使用されます。必要に応じて、サブクラスは BasicPermission の上に重ねて動作を実装できます。
BasicPermission サブクラスには、java.lang.RuntimePermission、java.security.SecurityPermission、 java.util.PropertyPermission、java.net.NetPermission などがあります。
このクラスのターゲットは、基本的には、さまざまなプロパティファイルのセットとしての Java プロパティの名前です。たとえば、java.home、os.name などです。ターゲットは、「*」(任意のプロパティ)、「a.*」(「a.」接頭辞を持つ名前の任意のプロパティ)、「a.b.*」などの形式で指定でき ます。ワイルドカードは、右端に 1 回だけ使用できます。このクラスは、BasicPermission の上に重ねて動作を実装する BasicPermission サブクラスの 1 つです。動作は、read と write です。それぞれの意味は、次のように定義されます。read アクセス権は、java.lang.System 内の
getProperty
メソッドを呼び出してプロパティ値を取得することを許可します。write アクセス権は、setProperty
メソッドを呼び出してプロパティ値を設定することを許可します。
RuntimePermission のターゲットは任意の文字列で表現でき、このターゲットに関連する動作はありません。たとえば、RuntimePermission("exitVM") は Java 仮想マシンを終了する権限を表します。
createClassLoader
getClassLoader
setContextClassLoader
setSecurityManager
createSecurityManager
exitVM
setFactory
setIO
modifyThread
stopThread
modifyThreadGroup
getProtectionDomain
readFileDescriptor
writeFileDescriptor
loadLibrary.{library name}
accessClassInPackage.{package name}
defineClassInPackage.{package name}
accessDeclaredMembers.{class name}
queuePrintJob
このクラスは、RuntimePermission と同じ考え方に基づいており、アクセス権には動作がありません。このクラスのターゲットには、次のようなものがあります。
accessClipboard
accessEventQueue
listenToAllAWTEvents
showWindowWithoutWarningBanner
このクラスには次のターゲットがあり、動作はありません。
requestPasswordAuthentication
setDefaultAuthenticator
specifyStreamHandler
リフレクト操作のためのアクセス権クラスです。ReflectPermission は、名前付きのアクセス権 (RuntimePermission と同様) で、動作はありません。現在定義されている名前は、次の 1 つだけです。
suppressAccessChecks
これは、リフレクトされたオブジェクトが使用される位置で実行され る、Java プログラミング言語の標準のアクセスチェック (public、default(package) アクセス、protected、private メンバに対するチェック) を無効にします。
このクラスには次のターゲットがあり、動作はありません。
enableSubclassImplementation
enableSubstitution
SecurityPermissions は、セキュリティ関連のオブジェクト (Security、Policy、Provider、Signer、Identity オブジェクトなど) へのアクセスを管理します。このクラスには次のターゲットがあり、動作はありません。
getPolicy
setPolicy
getProperty.{key}
setProperty.{key}
insertProvider.{provider name}
removeProvider.{provider name}
setSystemScope
setIdentityPublicKey
setIdentityInfo
printIdentity
addIdentityCertificate
removeIdentityCertificate
clearProviderProperties.{provider name}
putProviderProperty.{provider name}
removeProviderProperty.{provider name}
getSignerPrivateKey
setSignerKeyPair
このアクセス権では、すべてのアクセスが許可されます。すべての (つまり多数の) アクセスの許可が必要な作業をするシステム管理者の仕事を簡単にするために用意されています。セキュリティポリシーにすべてのアクセス権を繰り返し定義す るのは効率的ではありません。AllPermission は、将来定義される新しいアクセス権も許可します。
AuthPermission では、認証アクセス権と認証に関連するオブジェクト (Subject、SubjectDomainCombiner、LoginContext、および Configuration) を扱います。このクラスには次のターゲットがあり、動作はありません。doAs
doAsPrivileged
getSubject
getSubjectFromDomainCombiner
setReadOnly
modifyPrincipals
modifyPublicCredentials
modifyPrivateCredentials
refreshCredential
destroyCredential
createLoginContext.{name}
getLoginConfiguration
setLoginConfiguration
refreshLoginConfiguration
前述したように、アクセス権は互いに比較されることがあります。このような比較を簡単に行 えるように、各アクセス権クラスでimplies
メソッドを定義して、特定のアクセス権クラスと他のアクセス権クラスとの関係を表す必要があります。たとえば、 java.io.FilePermission("/tmp/*", "read") には java.io.FilePermission("/tmp/a.txt", "read") が含まれますが、java.net.NetPermission にはどれも含まれません。一見しただけでは分からない、暗黙の包含関係もあります。たとえば、あるアプレット にファイルシステム全体への書き込みアクセス権を与えるとします。これにより、このアプレットには、JVM 実行時環境を含むシステムのバイナリファイルの書き換えが許可されると考えられます。これは結果的に、そのアプレットにすべてのアクセス権を与えることを 意味します。
別の例として、あるアプレットにクラスローダを生成する実行時アクセス権を与える場 合、クラスローダはクリティカルな操作を実行できるので、結果的に、このアプレットにさらに多くのアクセス権を与えることになります。
危険を伴うアクセス権にはこれ以外に、システムプロパティの設定を許可するもの、 パッケージの定義やネイティブコードライブラリのローディングのための実行時アクセス権 (Java のセキュリティ構造はネイティブコードレベルでの悪意のある動作を防ぐように設計されていないため)、および AllPermission があります。
アクセス権の詳細については、
http://java.sun.com/j2se/sdk/1.2/docs/guide/security/permissions.html
を参照してください。アクセス権を必要とする Java 2 SDK 組み込みのメソッド、および特定のアクセス権の割り当てに伴う危険性について、一覧で説明されています。
新機能の追加または java.lang.RuntimePermission などのクラスへの追加のターゲットキーワードの導入により、Java 2 SDK に組み込まれているアクセス権を拡張することは、Sun Microsystems だけに許されています。これは一貫性を維持するために必要なことです。新しくアクセス権を作成するには、次の例のような方法をお勧めします。ABC 社のアプリケーション開発者が「TV を見る」ための新しいカスタムのアクセス権を作成するとします。
最初に、abstract クラス java.security.Permission (またはそのサブクラスのひとつ) を継承する新しいクラス com.abc.Permission、および com.abc.Permission を継承する新しいクラス com.abc.TVPermission を作成します。他のクラスの
implies
メソッドが正しく実装されるように注意してください。ただし、com.abc.TVPermission は中間の com.abc.Permission がなくても、直接 java.security.Permission を継承することはできます。
public class com.abc.Permission extends java.security.Permission
public class com.abc.TVPermission extends com.abc.Permission
次の図では、サブクラスの関係を示します。次に、アプリケーションパッケージにそれらの新規クラスを組み込みます。
特定のコードにこの新しいタイプのアクセス権を許可する場合、各ユーザはポリシー ファイルにエントリを追加します。(ポリシーファイルの構文の詳細については後述)。次に、5 チャンネルを見る (watch) 権限を http://java.sun.com/ のコードに付与するポリシーファイルの例を示します。
grant codeBase "http://java.sun.com/" {
permission com.abc.TVPermission "channel-5", "watch";
}
アプリケーションの資源管理コードで、アクセス権を与えるべきかどう
かのチェックを行うときは、com.abc.TVPermission をパラメータとして AccessController の checkPermission
メソッドを呼び出します。
com.abc.TVPermission tvperm = new
com.abc.TVPermission("channel-5", "watch");
AccessController.checkPermission(tvperm);
新しいアクセス権を追加するときは、新しい (アクセス権) クラスを作成します。 セキュリティマネージャに新しいメソッドを追加するのではないので注意してください。以前は、新しいタイプのアクセスをチェックするためには SecurityManager クラスに新しいメソッドを追加する必要がありました。channel-1:13 や channel-* など、より難解な TVPermissions を許可するには、これらの疑似名のセマンティクスの扱いを知っている TVPermissionCollection オブジェクトを実装する必要があるかもしれません。
新しいコードがアクセス制御の組み込みアルゴリズムを実行するためには、 AccessController クラスの
checkPermission
メソッドを呼び出してアクセス権チェックを起動します。ClassLoader や SecurityManager があるかどうかについては、必ずしも調べる必要はありません。ただし、インストールされているセキュリティマネージャクラスにそのアルゴリズムを任せる場 合は、SecurityManager.checkPermission
メソッドを呼び出します。
このクラスは、HTML のコードベースの概念を拡張して、コードの位置 (URL) だけでなくその位置からの署名付きコードの確認に使用する公開鍵を持つ証明書をカプセル化します。ただし、これは HTML ファイルの CodeBase タグと同じではないので注意してください。各証明書は java.security.cert.Certificate として表され、各 URL は java.net.URL として表されます。
どのソースからのコードにどのアクセス権が使用できるかを指定する Java アプリケーション環境のシステムセキュリティポリシーは、Policy オブジェクトで表されます。正確には、Policy クラスの abstract メソッドを実装した Policy サブクラスにより表現されます。ファイルの読み書きなど、アプレット (または SecurityManager の下で実行中のアプリケーション) がセキュリティの対象である動作を行うためには、そのアプレット (またはアプリケーション) はその動作のためのアクセス権を与えられていることが必要です。唯一の例外として、コードは必ず、同じ CodeSouce、およびその CodeSouce のサブディレクトリからのファイルの読み出し権を自動的に持ちます。この場合は、明示的なアクセス権は必要ありません。
Policy オブジェクトのインスタンスは、複数存在できますが、「有効な」インスタンスは常に 1 つだけです。現在インストールされている Policy オブジェクトは
getPolicy
メソッドの呼び出しにより取得でき、(Policy のリセット権を持つコードによる)setPolicy
メソッドの呼び出しによって変更できます。Policy オブジェクトに利用されるポリシー情報のソースの位置は、Policy の実装によって異なります。たとえば、ポリシーの設定は、単純な ASCII ファイル、Policy クラスの直列化バイナリファイル、データベースのどれかの形で保存できます。Policy のリファレンス実装の 1 つでは、情報を静的なポリシー設定ファイルから取得します。
Policy のリファレンス実装では、ポリシーは 1 つまたは複数のポリシー設定ファイル内に指定できます。設定ファイルは、特定のコードソースからのコードに許可されるアクセス権を示します。各設定ファイ ルは、UTF-8 方式で符号化する必要があります。ポリシー設定ファイルには、必ずエントリのリストが含まれます。1 つの keystore エントリと、0 以上の grant エントリを持たせることができます。
キーストアは、非公開鍵とその鍵に関連するデジタル証明書 (対応する公開鍵を認証する X.509 証明連鎖など) のデータベースです。キーストアの作成と管理には、keytool ユーティリティを使います。ポリシー設定ファイルで指定されているキーストアは、そのファイルの grant エントリで指定されている署名者の公開鍵を照合するために使用されます。署名者の別名を指定している grant エントリがある場合、または主体の別名を指定している grant エントリがある場合は、ポリシー設定ファイルには必ず keystore エントリを置きます (次を参照)。
現在、ポリシーファイルで指定できるキーストアエントリは 1 つだけで、2 つ目以降のキーストアエントリは無視されます。キーストアエントリは、ポリシーファイルの付与エントリの外であれば、どこに指定してもかまいません。 keystore エントリの構文は次のとおりです。
keystore "some_keystore_url", "keystore_type";
ここで、some_keystore_url にはキーストアの URL 位置を指定し、keystore_type にはキーストアのタイプを指定します。keystore_type の指定は任意で行います。指定しない場合、キーストアのタイプは、セキュリティプロパティファイルの keystore.type プロパティで指定されたものと見なされます。URL は、ポリシーファイルとの相対位置です。たとえば、セキュリティプロパティファイルの中でポリシーファイルが次のように指定されているとします。
policy.url.1=http://foo.bar.com/blah/some.policy
ポリシーファイルに次のようなエントリがある場合は、
keystore ".keystore";
キーストアは次の位置からロードされます。
http://foo.bar.com/blah/.keystore
URL は絶対位置でも指定できます。キーストアのタイプが定義するのは、キーストア情報の記憶領域とデータ形式、および キーストア内の非公開鍵とキーストア自体の安全性を保護するために使用されるアルゴリズムです。Sun Microsystems がサポートするデフォルトのタイプは、Sun Microsystems に所有権があるキーストアタイプ名「JKS」です。
ポリシーファイルの各 grant エントリは、本質的には 1 つの CodeSource とそのアクセス権から成ります。実際には、CodeSource は 1 つの URL と証明書のセットから成りますが、ポリシーファイルのエントリの内容は URL と署名者名のリストです。システムは、指定された署名者の証明書を決定するためにキーストアを調べたあとで、対応する CodeSource を生成します。
ポリシーファイルの各 grant エントリは、次の形式のどれかです。ここで先頭の grant は予約語で、新しいエントリの開始を示し、括弧の中にはオプション項目が示されます。各エントリ内の先頭の permission も予約語で、そのエントリ内で新しいアクセス権の記述が開始されることを示します。各 grant エントリは、指定したコードソースと主体に 1 組のアクセス権を与えます。
grant [SignedBy "signer_names"] [, CodeBase "URL"]
[, Principal [principal_class_name] "principal_name"]
[, Principal [principal_class_name] "principal_name"] ... {
permission permission_class_name [ "target_name" ]
[, "action"] [, SignedBy "signer_names"];
permission ...
};
コンマの直前または直後には、空白文字を入れてもかまいません。アク セス権クラスの名前は、Java の完全修飾形のクラス名 (java.io.FilePermission など) でなければならず、省略 (FilePermission など) はできません。動作のフィールドは、省略可能なオプションフィールドで、クラスが動作を必要としな い場合には省略できます。このフィールドを指定する場合は、必ずターゲットフィールドの直後に置きます。
コードベース URL の正確な意味は、最後の文字に依存します。末尾が「/」のコードベースは、指定されたディレクトリ内のすべてのクラスファイル (JAR ファイルでない) に一致します。末尾が「/*」のコードベースは、そのディレクトリ内にあるすべてのファイル (クラスファイルと JAR ファイルの両方) に一致します。末尾に「/-」の付いたコードベースは、ディレクトリのすべてのファイル (クラスファイルと JAR ファイルの両方)、および再帰的にそのディレクトリのサブディレクトリにあるすべてのファイルを表します。
CodeBase フィールド (URL) は、省略可能なオプションフィールドで、省略した場合は「任意のコードベース」が指定されます。
最初の署名者名フィールドは、署名者に関連付けられている 1 組の公開鍵 (キーストアの証明書内にある) に、別の機構を通じてマッピングされた文字列の別名です。これらの鍵は、ある署名付きのクラスが本当にこれらの署名者によって署名されたかどうかを確認す るために使用されます。
この署名者フィールドは、複数の署名者の名前をコンマ区切りの文字列で指定できま す。たとえば、「Adam,Eve,Charles」は、Adam と Eve と Charles によって署名されることを意味します (各者の関係は OR ではなく AND)。
このフィールドは省略可能なオプションフィールドで、省略した場合は「任意の署名 者」、つまり「コードが署名付きかどうかは不問」の指定になります。
Permission エントリ内にある 2 つ目の署名者フィールドは、キーストアエントリの別名を表します。このキーストアエントリには、そのアクセス権クラスを実装しているバイトコードへの署名 に使用される非公開鍵に対応する公開鍵が含まれます。このアクセス権エントリが有効なのは (つまりこのエントリに基づいてアクセス制御権が与えられるのは)、バイトコードの実装が別名によって正しく署名されていることが確認された場合だけで す。
主体の値は class_name と principal_name のペアを指定します。このペアは、実行中のスレッドの主体セット内にある必要があります。主体セットは、Subject によって実行するコードに関連付けられます。主体フィールドは省略可能です。省略した場合は、「任意の主体」という意味になります。
principal class_name/principal_name のペアが単一引用符で囲まれた文字列として指定される場合は、キーストアの別名として扱われます。キーストアは別名を経由して X509 証明書を調査し、問い合わせます。キーストアがある場合は、principal_class は自動的に
javax.security.auth.x500.X500Principal
として扱われ、principal_name は証明書で名前を識別された被認証者として自動的に扱われます。X509 証明書のマッピングが見つからない場合は、grant エントリはすべて無視されます。
PolicyFile -> PolicyEntry | PolicyEntry; PolicyFile
PolicyEntry -> grant {PermissionEntry}; |
grant SignerEntry {PermissionEntry} |
grant CodebaseEntry {PermissionEntry} |
grant PrincipalEntry {PermissionEntry} |
grant SignerEntry, CodebaseEntry {PermissionEntry} |
grant CodebaseEntry, SignerEntry {PermissionEntry} |
grant SignerEntry, PrincipalEntry {PermissionEntry} |
grant PrincipalEntry, SignerEntry {PermissionEntry} |
grant CodebaseEntry, PrincipalEntry {PermissionEntry} |
grant PrincipalEntry, CodebaseEntry {PermissionEntry} |
grant SignerEntry, CodebaseEntry, PrincipalEntry {PermissionEntry} |
grant CodebaseEntry, SignerEntry, PrincipalEntry {PermissionEntry} |
grant SignerEntry, PrincipalEntry, CodebaseEntry {PermissionEntry} |
grant CodebaseEntry, PrincipalEntry, SignerEntry {PermissionEntry} |
grant PrincipalEntry, CodebaseEntry, SignerEntry {PermissionEntry} |
grant PrincipalEntry, SignerEntry, CodebaseEntry {PermissionEntry} |
keystore "url"
SignerEntry -> signedby (a comma-separated list of strings)
CodebaseEntry -> codebase (a string representation of a URL)
PrincipalEntry -> OnePrincipal | OnePrincipal, PrincipalEntry
OnePrincipal -> principal [ principal_class_name ] "principal_name" (a principal)
PermissionEntry -> OnePermission | OnePermission PermissionEntry
OnePermission -> permission permission_class_name
[ "target_name" ] [, "action_list"]
[, SignerEntry];
ここで、いくつか例を示します。次のポリシーでは、Roland によって署名されたコードにアクセス権 a.b.Foo を与えます。
grant signedBy "Roland" {
permission a.b.Foo;
};
次の例では、すべてのコード (署名者やコードベースにかかわらず) に FilePermission を与えます。
grant {
permission java.io.FilePermission ".tmp", "read";
};
次の例では、Li と Roland の両者によって署名されたコードに、2 つのアクセス権を与えます。
grant signedBy "Roland,Li" {
permission java.io.FilePermission "/tmp/*", "read";
permission java.util.PropertyPermission "user.*";
};
次の例では、Li によって署名され http://java.sun.com からロードされたコードに、2 つのアクセス権を与えます。
grant codeBase "http://java.sun.com/*", signedBy "Li" {
permission java.io.FilePermission "/tmp/*", "read";
permission java.io.SocketPermission "*", "connect";
};
次の例では、com.abc.TVPermission を実装したバイトコードが確実に Li によって署名されている場合にだけ、Li と Roland の両者によって署名されたコードに 2 つのアクセス権を与えます。
grant signedBy "Roland,Li" {
permission java.io.FilePermission "/tmp/*", "read";
permission com.abc.TVPermission "channel-5", "watch",
signedBy "Li";
};
2 つ目の署名者フィールドを使う目的は、インストールされた Java Runtime にそのアクセス権クラスが存在しない場合に、不正行為を防ぐことです。たとえば、com.abc.TVPermission クラスのコピーは、リモートの JAR アーカイブの一部としてダウンロードできますが、ユーザポリシーにそれを参照するエントリが含まれることがあります。アーカイブは短命なので、 com.abc.TVPermission クラスが別の Web サイトから再びダウンロードされる可能性があります。このときのユーザポリシー内のアクセス権エントリの存在は、クラスのバイトコードの最初のコピーに対 するユーザの信頼を反映するものであるため、2 つ目のコピーが認証済みであることは重要です。認証を確実なものにするために、バイトコードの最初のコピー (のハッシュ値) を保存して 2 つ目のコピーと比べる方法ではなく、デジタル署名を使用する理由は、アクセス権クラスの作者が合法的にクラスファイルを更新して新しい設計や実装を反映す ることがあるからです。
注: ファイルパスの文字列は、プラットフォームごとに異なる形式で指定する必要があります。これは、将来、全プラットフォームに共通するファイル記述言語がで きるまでは必要です。上記の各例は、Solaris システム用の文字列で記述されています。Windows システムでは、文字列中で直接ファイルパスを指定する場合は、「\」記号は次のように 2 つの「\」記号で表します。
grant signedBy "Roland" {
permission java.io.FilePermission "C:\\users\\Cathy\\*", "read";
};
文字列はトークナイザ (java.io.StreamTokenizer) によって処理されますが、そこでは「\」を使ったエスケープ文字列 (改行を表す「\n」など) の使用が許されるので、単一の「\」記号を表すためには 2 つの連続した「\」記号を使う必要があります。トークナイザが FilePermission のターゲット文字列の処理を終え、2 つの連続した「\」記号を単一の「\」記号に変換すると、結果は次の実際のパスになります
"C:\users\Cathy\*"
最後に、主体ベースの grant エントリの例を示します。grant principal javax.security.auth.x500.X500Principal "cn=Alice" {これにより、X500Principal のアクセス権「cn=Alice」として実行するコードは /home/Alice を読み取ることも書き込むことも可能になります。
permission java.io.FilePermission "/home/Alice", "read, write";
};次の例は、codesource 情報と principal 情報を持つ grant 文を表しています。
grant codebase "http://www.games.com",これにより、「Duke」によって署名され、www.games.com からダウンロードされたコードが「cn=Alice」によって実行されると、/tmp/games ディレクトリへの読み取り権と書き込み権が与えられます。
signedBy "Duke",
principal javax.security.auth.x500.X500Principal "cn=Alice" {
permission java.io.FilePermission "/tmp/games", "read, write";
};次の例は、キーストアの別名を置き換える grant 文を示しています。
keystore "http://foo.bar.com/blah/.keystore";"alice" は次のアクセス権に置き換えられます。
grant principal "alice" {
permission java.io.FilePermission "/tmp/games", "read, write";
};javax.security.auth.x500.X500Principal "cn=Alice"ただし、キーストアの別名 alice に関連付けられた X.509 証明書が被認証者の識別名「cn=Alice」を持っていることが前提となります。これにより、X500Principal のアクセス権「cn=Alice」によって実行されるコードに、/tmp/games ディレクトリへの読み取り権と書き込み権が与えられます。
ポリシーファイルとセキュリティプロパティファイルでは、プロパティの展開が可能です。プロパティの展開は、シェル内の変数の展開と似ています。ポリシーファイルまたはセキュリ ティプロパティファイルに次のような文字列がある場合、
permission java.io.FilePermission "${user.home}", "read";
この場合、「${user.home}」は、システムプロパティ user.home の値に展開されます。user.home の値が /home/cathy である場合、上の記述は下の記述と同じになります。
permission java.io.FilePermission "/home/cathy", "read";
プラットフォームにより異なるポリシーファイルを使いやすくするため に、特殊表記「${/}」を使用できます。これは「${file.separator}」へのショートカットで、次のようにしてアクセス権を定義できま す。
permission java.io.FilePermission "${user.home}${/}*", "read";
Solaris システムで、user.home が /home/cathy であれば、これは次のように変換されます。
permission java.io.FilePermission "/home/cathy/*", "read";
Windows システムで、user.home が C:\users\cathy であれば、次のように変換されます。
permission java.io.FilePermission "C:\users\cathy\*", "read";
特殊なケースとして、コードベースのプロパティを以下のように記述す ると、
grant codeBase "file:/${java.home}/lib/ext/"
file.separator 文字は自動的にすべて「/」に置き換えられます。コードベースが URL であるため、「/」に置き換えるのが望ましいといえます。したがって、Windows システムで java.home が C:\j2sdk1.2 に設定されていても、次のように変換されます。
grant codeBase "file:/C:/j2sdk1.2/lib/ext/"
したがって、コードベース文字列では ${/} を使う必要はありません。また、使うべきではありません。プロパティの展開は、ポリシーファイルで二重引用符の使用が許可される位置ではどこ でも発生します。これには、署名者、コードベース、ターゲット名、動作の各フィールドがあります。
プロパティの展開が許可されるかどうかは、セキュリティプロパティファイルの 「policy.expandProperties」プロパティの値によって決まります。このプロパティの値が true (デフォルト) の場合は、展開が許可されます。
"${user.${foo}}"
この例では、「foo」プロパティが「home」に設定されている場 合であっても、エラーになります。その理由は、プロパティの構文解析では入れ子のプロパティを認識できないからです。プロパティ構文解析プログラムは、最 初の「${」を見つけたら、次に最初の「}」を探し、その結果 (この場合は「${user.$foo}」) をプロパティと解釈しようと試みます。しかし、そのようなプロパティがない場合はエラーになります。注: 付与エントリ、アクセス権エントリ、またはキーストアエントリで展開できないプロパティがある場合、そのエントリは無視されます。たとえば、次のようにシ ステムプロパティ foo が定義されていない場合、
grant codeBase "${foo}" {
permission ...;
permission ...;
};
この付与エントリ内のアクセス権はすべて無視されます。また、次のよ うな場合、
grant {
permission Foo "${foo}";
permission Bar;
};
permission Foo... エントリだけが無視されます。また、次のように指定されている場合、
keystore "${foo}";
キーストアエントリは無視されます。注: Windows システムでは、文字列中で直接ファイルパスを指定する場合は、「\」記号は次のように 2 つの「\」記号で表します。
"C:\\users\\cathy\\foo.bat"
文字列はトークナイザ (java.io.StreamTokenizer) によって処理されますが、そこでは「\」を使ったエスケープ文字列 (改行を表す「\n」など) の使用が許されるので、単一の「\」記号を表すためには 2 つの連続した「\」記号を使う必要があります。トークナイザが文字列の処理を終え、2 つの連続した「\」記号を単一の「\」記号に変換すると、結果は次の実際のパスになります。
"C:\users\cathy\foo.bat"
文字列中のプロパティの展開は、トークナイザがその文字列の処理を完 了したあとに行われます。たとえば、次のような文字列があるとします。
"${user.home}\\foo.bat"
上のような文字列の場合、まずトークナイザが文字列を処理して、2 つの「\」記号を 1 つの「\」記号に置き換えると次のようになります。
"${user.home}\foo.bat"
次に、user.home の値が C:\users\cathy であれば、${user.home} プロパティが展開されて次のようになります。
"C:\users\cathy\foo.bat"
もちろん、プラットフォームに依存しないために、明示的にスラッシュを使うのではなく、${/} プロパティを使って次のように指定するほうが望ましいと言えます。
"${user.home}${/}foo.bat"
ポリシーファイルでは一般化された形式の展開もサポートされています。たとえば、アクセス権名が次の形式の文字列を含んでいるとします。${{protocol:protocol_data}}このような文字列がアクセス権名に含まれる場合、protocol の値によって実行される展開のタイプが決まり、protocol_data は展開を実行するために使用されます。protocol_data は空にすることもできます。空の場合は、上記の文字列は次のような単純な形式になります。${{protocol}}デフォルトのポリシーファイルの実装では、次の 2 つのプロトコルがサポートされます。
- ${{self}}
プロトコル self は、${{self}} 文字列全体を 1 つ以上の主体クラスと主体名のペアに置き換えることを示します。実際に実行される置き換えは、permission を含む grant 句によって決まります。
grant 句に主体の情報が含まれていない場合は、permission は無視されます。主体ベースの grant 句のコンテキストでは、ターゲット名に ${{self}} を含む permission のみが有効です。たとえば、次の grant 句内の BarPermission は常に無視されます。
grant codebase "www.foo.com", signedby "duke" {grant 句に主体情報が含まれている場合は、${{self}} がその主体情報に置き換えられます。たとえば、次の grant 句の BarPermission 内の ${{self}} は、javax.security.auth.x500.X500Principal "cn=Duke" に置き換えられます。
permission BarPermission "...${{self}} ...";
};
grant principal javax.security.auth.x500.X500Principal "cn=Duke" {grant 句内にコンマで区切られた主体のリストがある場合、${{self}} は、そのコンマで区切られた主体のリストに置き換えられます。grant クラス内の主体クラスと主体名がどちらもワイルドカードになっている場合、${{self}} は、現在の
permission BarPermission "...${{self}} ...";
};
AccessControlContext
内のSubject
に関連付けられたすべての主体に置き換えられます。次の例は、self と キーストアの別名 の置き換えの両方を含むシナリオを示しています。
keystore "http://foo.bar.com/blah/.keystore";上の例では、初めに「duke」が次のように展開されます。
grant principal "duke" {
permission BarPermission "...${{self}} ...";
};
javax.security.auth.x500.X500Principal "cn=Duke"キーストアの別名に関連付けられた X.509 証明書 duke が「cn=Duke」という被認証者識別名を持っているものとします。次に、${{self}} が、grant 句内で展開されたその同じ主体情報に置き換えられます。javax.security.auth.x500.X500Principal "cn=Duke".- ${{alias:alias_name}}
プロトコル alias は、
java.security.KeyStore
別名の置換を示します。KeyStore entry に指定されたKeyStore
が使用されます。alias_name はKeyStore
の別名を表します。${{alias:alias_name}} は javax.security.auth.x500.X500Principal "DN" で置き換えられます。ここで、DN は alias_name に属する証明書の被認証者識別名です。次に例を示します。keystore "http://foo.bar.com/blah/.keystore";上の例の別名 duke に関連付けられている X.509 証明書は、キーストア foo.bar.com/blah/.keystore から取得されます。duke の証明書が被認証者識別名として "o=dukeOrg, cn=duke" を指定し、${{alias:duke}} が javax.security.auth.x500.X500Principal "o=dukeOrg, cn=duke" で置き換えられるものとします。
grant codebase "www.foo.com" {
permission BarPermission "...${{alias:duke}} ...";
};
次のエラー条件に該当する場合、アクセス権エントリは無視されます。
- キーストアエントリが指定されていない
- alias_name が指定されていない
- alias_name の証明書を取得できない
- 取得される証明書が X.509 証明書ではない
主体が特定のコードソースからクラスを実行するとき、セキュリティ機構はポリシーオブジェ クトを調べてどのアクセス権を与えるべきかを決定します。この決定は、VM にインストールされた Policy オブジェクトのgetPermissions
メソッドまたはimplies
メソッドを呼び出すことにより行われます。ワイルドカード「*」の使用が認められているなどの理由により、 ProtectionDomain 内のコードソースがポリシー内の複数のエントリのコードソースに一致することがあります。
1. コードが署名付きの場合は、公開鍵をマッチングする
2. ある鍵がポリシー内で認識されない場合は、その鍵を無視する
すべての鍵が無視された場合は、そのコードを署名なしとして扱う
3. 鍵がマッチした場合、または署名者が指定されていない場合は、
それらの鍵のポリシー内のすべての URL のマッチングを試みる
}
4. 鍵がマッチした場合 (または署名者が指定されていない場合)、
または URL がマッチした場合 (またはコードベースが指定されていない場合) は、
ポリシー内のすべての主体と
現在実行中のスレッドに関連付けられている主体とのマッチをすべて試みる
5. 鍵、URL、または主体のどれかがマッチしない場合は、組み込みのデフォルトアクセス権
(オリジナルの sandbox アクセス権) を使用する
ポリシーエントリのコードベース URL の正確な意味は、最後の文字に依存します。末尾が「/」のコードベースは、指定されたディレクトリ内のすべてのクラスファイル (JAR ファイルでない) に一致します。末尾が「/*」のコードベースは、そのディレクトリ内にあるすべてのファイル (クラスファイルと JAR ファイルの両方) に一致します。末尾に「/-」の付いたコードベースは、ディレクトリのすべてのファイル (クラスファイルと JAR ファイルの両方)、および再帰的にそのディレクトリのサブディレクトリにあるすべてのファイルを表します。たとえば、ポリシー内に http://java.sun.com/ がある場合は、その Web サイト上のすべてのコードベースはこのポリシーエントリにマッチします。マッチするコードベースには、http: //java.sun.com/j2se/sdk/ および http://java.sun.com/people/gong/appl.jar があります。
複数のエントリがマッチする場合は、それらのエントリ内のすべてのアクセス権が与え られます。つまり、アクセス権は追加割り当てできます。たとえば、鍵 A で署名されたコードにアクセス権 X が与えられ、鍵 B で署名されたコードにはアクセス権 Y が与えられており、特定のコードベースが指定されていない場合、A と B の両方で署名されたコードにはアクセス権 X と Y の両方が与えられます。同様に、コードベースが http://java.sun.com/- のコードにアクセス権 X が与えられ、http://java.sun.com/people/* のコードにアクセス権 Y が与えられており、特定の署名者が指定されていない場合は、http://java.sun.com/people/applet.jar からのアプレットには X と Y の両方のアクセス権が与えられます。
ここでの URL のマッチングは、純粋に構文上のマッチングです。たとえば、あるポリシーに URL ftp://ftp.sun.com を指定するエントリがあるとします。このようなエントリは、Java コードを直接 ftp からダウンロードして実行できる場合にだけ有用です。
ローカルファイルシステムの URL を指定する場合、ファイルの URL を使用できます。たとえば Solaris システムの /home/cathy/temp ディレクトリ内のファイルを指定するには、次のようにします。
"file:/home/cathy/temp/*"
Windows システムの C ドライブにある temp ディレクトリ内のファイルを指定するには、次のようにします。
"file:/c:/temp/*"
注: コードベースの URL には、プラットフォームにかかわらず、必ず「/」(「\」記号ではなく) を使用してください。
"/home/gong/bin/MyWonderfulJava"
Policy のリファレンス実装では、ポリシーは 1 つまたは複数のポリシー設定ファイル内に指定できます。設定ファイルは、特定のコードソースからのコードに許可されるアクセス権を指定します。ポリシーファイルは、単純なテキストエディタ、または後述する PolicyTool ユーティリティを使って作成できます。
{java.home}/lib/security/java.policy (Solaris)
{java.home}\lib\security\java.policy (Windows)
ここで java.home は Java 2 SDK がインストールされているディレクトリを示すシステムプロパティです。
{user.home}/.java.policy (Solaris)
{user.home}\.java.policy (Windows)
ここで user.home は、ユーザのホームディレクトリを示すシステムプロパティです。Policy の初期化時は、最初にシステムポリシーがロードされ、次にユーザポリシーが追加されます。どちらのポリシーも存在しない場合は、組み込みポリシーが使われ ます。組み込みポリシーは、オリジナルのサンドボックス (sandbox) ポリシーと同じものです。
{java.home}/lib/security/java.security (Solaris)
{java.home}\lib/security\java.security (Windows)
ポリシーファイルの位置は、次の形式の名前のプロパティ値として指定 されます。
policy.url.n
ここで、n は数値です。次に示す形式の行で、それぞれのプロパティの値を指定します。
policy.url.n=URL
ここで、URL は URL を指定します。たとえば、デフォルトのシステムおよびユーザポリシーファイルは、セキュリティプロパティファイルに次のように指定されています。
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
「http://」の形のものも含めて、実際には複数の URL を指定でき、指定したポリシーファイルのすべてがロードされます。また、上に示したポリシーファイルの指定のうち、2 番目のポリシーファイルの指定をコメントアウトするか、あるいは修正すれば、デフォルトユーザポリシーファイルの読み込みを無効にすることができます。アルゴリズムは policy.url.1 から処理を開始して、番号が連続して増加している間、URL が見つからなくなるまで処理を続けます。したがって、policy.url.1 と policy.url.3 がある場合、policy.url.3 は読み込まれません。
アプリケーションの実行を呼び出すときに、追加のまたは別のポリシーファイルを指定 することもできます。この場合は、-Djava.security.policy コマンド行引数を使って java.security.policy プロパティの値を設定します。たとえば、次のように指定します。
java -Djava.security.manager -Djava.security.policy=pURL SomeApp
ここで pURL は、ポリシーファイルの位置を示す URL です。指定されたポリシーファイルは、セキュリティプロパティファイルで指定されたすべてのポリシーファイルに追加されてロードされます。引数 -Djava.security.manager により、デフォルトのセキュリティマネージャが確実にインストールされるので、「アプレットおよびアプリケーションのセキュリティ管理」の説明にあるよう に、アプリケーションはポリシーチェックを受けます。アプリケーション SomeApp がセキュリティマネージャをインストールする場合は、-Djava.security.manager 引数を指定する必要はありません。次のように、「==」の付いた形を使うと、ここで指定されたポリシーファイルだけが ロードされ、他のポリシーファイルは無視されます。
java -Djava.security.manager -Djava.security.policy==pURL SomeApp
アプレットビューアにポリシーファイルを渡す場合も、次のように -Djava.security.policy 引数を使います。
appletviewer -J-Djava.security.policy=pURL myApplet
注: セキュリティプロパティファイルで policy.allowSystemProperty プロパティに false が設定されている場合は、-Djava.security.policy のポリシーファイルの値は (java コマンドと appletviewer コマンドのどちらの場合も) 無視されます。policy.allowSystemProperty プロパティのデフォルト値は true です。
Policy クラスの現在の設計は、すべての場合に適用できる包括的なものではありません。その点については考察が重ねられ、多くの場合に適したメソッドの呼び出しが 確実にできるよう部分的な改良が進められています。それまでの間、デフォルトのポリシークラスを別のポリシークラスに置き替えることができます。ただし、 置き換えるポリシークラスが abstract Policy クラスのサブクラスであり、getPermissions
メソッド (および必要に応じて他のメソッド) を実装していることが条件です。Policy のリファレンス実装は、policy.provider セキュリティプロパティ (セキュリティプロパティファイル内) の値を、目的の Poliocy 実装クラスの完全修飾形の名前に設定し直すことにより変更できます。セキュリティプロパティファイルは、次の名前のファイルです。
{java.home}/lib/security/java.security (Solaris)
{java.home}\lib\security\java.security (Windows)
{java.home} は、実行環境がインストールされたディレクトリ (Java 2 SDK 内の jre ディレクトリ、または Java 2 Runtime Environment の最上位ディレクトリ) を参照します。
policy.provider=sun.security.provider.PolicyFile
カスタマイズするには、このプロパティの値が別のクラスを指すよう に、次のように変更します。
policy.provider=com.mycom.MyPolicy
MyPolicy クラスは、java.security.Policy のサブクラスでなければなりません。このような、ポリシークラスのオーバーライドはあくまでも一時的な解決策であり、より包括的なポリシー API が設計されれば不要になります。
これは、新しい例外クラスで、java.lang.Exception のサブクラスです。これは、セキュリティおよびセキュリティパッケージ関連の例外を 2 種類にすることを意図して追加されました。
このような例外は、ある種のセキュリティ違反が検出された場合にだけスローされます。たと えば、あるコードがアクセス権のないファイルにアクセスしようとする場合に、この種の例外がスローされます。アプリケーション開発者は、任意でそれらの例 外をキャッチできます。
これらの例外はセキュリティに関連したものですが、必要不可欠というわけではありません。 たとえば、無効な鍵を渡すことはセキュリティ違反ではないと考えられますが、その鍵は開発者によりキャッチおよび処理される必要があります。RuntimeException のサブクラスである java.security パッケージ内に、現時点で 2 つの例外があります。下位互換性により、現時点ではこれらの例外は変更できません。将来この問題を再検討します。