| 目次 | 前の項目 | 次の項目 | Java セキュリティアーキテクチャ |
Permission クラスは、システムリソースへのアクセス権を表します。java.security.Permission クラスは abstract クラスで、このクラスから、特定のアクセス権を表すサブクラスが作られます。アクセス権の例としては、次のコードを使うと /tmp ディレクトリの abc というファイルの読み取り権が得られます。
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>>」は、どちらもファイルシステム全体を指します (利用可能なファイルシステムであれば、複数のファイルシステムを指すこともできる) が、別のオペレーティングシステム (MS 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 Virtual Machine を終了する権限を表します。
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 は、将来定義される新しいアクセス権も許可します。
前述したように、アクセス権は互いに比較されることがあります。 このような比較を簡単に行えるように、各アクセス権クラスで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 の下で実行中のアプリケーション) がセキュリティの対象である動作を行うためには、そのアプレット (またはアプリケーション) はその動作のためのアクセス権を与えられていることが必要です。唯一の例外として、コードは必ず、同じコードソース、およびそのコードソースのサブディレクトリからのファイルの読み出し権を自動的に持ちます。 この場合は、明示的なアクセス権は必要ありません。
Policy オブジェクトのインスタンスは、複数存在できますが、「有効な」インスタンスは常に 1 つだけです。現在インストールされている Policy オブジェクトは
getPolicyメソッドの呼び出しにより取得でき、(Policy のリセット権を持つコードによる)setPolicyメソッドの呼び出しによって変更できます。現在のポリシーは、保護ドメインがアクセス権セットを初期化するときに ProtectionDomain によって調べられます。保護ドメインが引き渡す CodeSource オブジェクトには、コードベース (URL) と証明書属性が含まれます。Policy オブジェクトは、グローバルなポリシーを評価して、指定されたコードソースからのコードに許可されるアクセス権を指定する Permissions オブジェクトを返します。
Policy オブジェクトに利用されるポリシー情報のソース位置は、Policy の実装によって異なります。たとえば、ポリシーの設定は、単純な ASCII ファイル、Policy クラスの直列化バイナリファイル、データベースのどれかの形で保存できます。Policy のデフォルトの実装の 1 つでは、情報を静的なポリシー設定ファイルから取得します。
Policy オブジェクトは、ポリシーの決定に関与しません。このオブジェクトは単に、持続性のあるポリシー設定の表現です。
デフォルトの Policy 実装では、ポリシーは 1 つまたは複数のポリシー設定ファイル内に指定できます。設定ファイルは、特定のコードソースからのコードに許可されるアクセス権を示します。ポリシー設定ファイルには、必ずエントリのリストが含まれます。1 つの keystore エントリと、0 以上の grant エントリを持たせることができます。
キーストアは、非公開鍵とその鍵に関連するデジタル証明書 (対応する公開鍵を認証する X.509 証明連鎖など) のデータベースです。キーストアの作成と管理には、keytool ユーティリティを使います。ポリシー設定ファイルで指定されているキーストアは、そのファイルの grant エントリで指定されている署名者の公開鍵を照合するために使用されます。署名者の別名を指定している grant エントリがある場合は、ポリシー設定ファイルには必ず keystore エントリを置きます (次を参照)。
この時点で、ポリシーファイルに存在できる keystore エントリはただ 1 つで (その他の keystore エントリは最初のエントリが無視されたあとに現れる)、そのファイルの grant エントリの外側のどこにあってもかまいません。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"] {
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 つ目の署名者フィールドは、キーストアエントリの別名を表します。 このキーストアエントリには、そのアクセス権クラスを実装しているバイトコードへの署名に使用される非公開鍵に対応する公開鍵が含まれます。このアクセス権エントリが有効なのは (つまりこのエントリに基づいてアクセス制御権が与えられるのは)、バイトコードの実装が別名によって正しく署名されていることが確認された場合だけです。
CodeBase フィールドと SignedBy フィールドの順序は任意です。
Policy ファイル形式の、非公式の BNF 文法の例を次に示します。 ここで小文字で始まる語はターミナル語です。
PolicyFile -> PolicyEntry | PolicyEntry; PolicyFile
PolicyEntry -> grant {PermissionEntry}; |
grant SignerEntry {PermissionEntry} |
grant CodebaseEntry {PermissionEntry} |
grant SignerEntry, CodebaseEntry {PermissionEntry} |
grant CodebaseEntry, SignerEntry {PermissionEntry} |
keystore "url"
SignerEntry -> signedby (a comma-separated list of strings)
CodebaseEntry -> codebase (a string representation of a URL)
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\*"
ポリシーファイルとセキュリティプロパティファイルでは、プロパティの展開が可能です。プロパティの展開は、シェル内の変数の展開と似ています。ポリシーファイルまたはセキュリティプロパティファイルに次のような文字列がある場合、
この文字列は指定されたシステムプロパティの値まで展開されます。次に例を示します。
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 ...;
};
次の場合は、permission Foo... エントリだけが無視されます。
grant {
permission Foo "${foo}";
permission Bar;
};
また次の場合は、keystore エントリが無視されます。
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"
特定のコードソースから新しいクラスをロードするとき、セキュリティ機構はポリシーオブジェクトを調べてどのアクセス権を与えるべきかを決定します。この決定は、VM にインストールされた Policy オブジェクトのgetPermissionsメソッドを呼び出すことにより行われます。ワイルドカード「*」の使用が認められているなどの理由により、コードソースがポリシー内の複数のエントリのコードソースに一致することがあります。
ポリシー内の適切なアクセス権セットを探すために、次のアルゴリズムが使用されます。
1. コードが署名付きの場合は、公開鍵をマッチングする
2. ある鍵がポリシー内で認識されない場合は、その鍵を無視する
すべての鍵が無視された場合は、そのコードを署名なしとして扱う
3. 鍵がマッチした場合、または署名者が指定されていない場合は、{
それらの鍵のポリシー内のすべての URL のマッチングを試みる
}
4. 鍵と 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 つまたは複数のポリシー設定ファイル内に指定できます。設定ファイルは、特定のコードソースからのコードに許可されるアクセス権を指定します。ポリシーファイルは、単純なテキストエディタ、または後述する Policy Tool ユーティリティを使って作成できます。
デフォルトでは、システム全体のポリシーファイルが 1 つあり、ユーザポリシーファイルが 1 つあります。
デフォルトでは、システムポリシーファイルは次の位置にあります。
{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 つの例外があります。下位互換性により、現時点ではこれらの例外は変更できません。将来この問題を再検討します。