目次 | 前の項目 | 次の項目 | Java セキュリティアーキテクチャ |
Permission クラスは、システムリソースへのアクセス権を表します。java.security.Permission クラスは抽象クラスで、このクラスから、特定のアクセス権を表すサブクラスが作られます。アクセス権の例としては、次のコードを使うと /tmp ディレクトリの abc というファイルの読み取り権が得られます。
perm = new java.io.FilePermission("/tmp/abc", "read");
新しいアクセス権は、Permission クラス、またはそのサブクラスの 1 つ (java.security.BasicPermission など) です。サブクラス化されたアクセス権 (BasicPermission 以外のアクセス権) は通常、それ自体のパッケージに属します。したがって、FilePermission は java.io パッケージにあります。すべての新規 Permission クラスに実装する必要のある重要な抽象メソッドが、
implies
メソッドです。基本的に、「a implies b」は、アクセス権 a を与えられている者は、当然アクセス権 b を与えられるということを意味します。この点は、アクセス制御の決定の際に重要です。抽象クラス java.security.Permission には、java.security.PermissionCollection という名前の抽象クラスと java.security.Permissions という final クラスが関連します。
java.security.PermissionCollection クラスは、1 つのカテゴリの Permission オブジェクトのコレクション (複製可能なセット) を表します。ファイルのアクセス権などのように、アクセス権を PermissionCollection オブジェクトに任意の順序で追加できる場合は、
implies
メソッドが呼び出されたときに、PermissionCollection オブジェクトが確実に正しいセマンティクスに従うことが重要です。java.security.Permissions クラスは、Permission オブジェクトのコレクションを表します。つまり、異種のアクセス権のスーパーコレクションです。
アプリケーションには、システムがサポートしているアクセス権の新規のカテゴリを自由に追加できます。アプリケーションに特定のアクセス権を追加する方法については、あとで説明します。
以降は、すべての組み込みアクセス権クラスの構文とセマンティクスについて説明します。
この抽象クラスは、すべてのアクセス権の上位クラスです。すべてのアクセス権に必要な必須機能を定義します。アクセス権の各インスタンスは、通常、コンストラクタに 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>>」は、どちらもファイルシステム全体を指します (利用可能なファイルシステムであれば、複数のファイルシステムを指すこともできる) が、別の OS (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/products/jdk/1.2/docs/guide/security/permissions.html
を参照してください。アクセス権を必要とする JDK 組み込みのメソッド、および特定のアクセス権の割り当てに伴う危険性について、一覧で説明されています。
新機能の追加または java.lang.RuntimePermission などのクラスへの追加のターゲットキーワードの導入により、JDK に組み込まれているアクセス権を拡張することは、Sun Microsystems だけに許されています。これは一貫性を維持するために必要なことです。新しくアクセス権を作成するには、次の例のような方法をお勧めします。ABC 社のアプリケーション開発者が「TV を見る」ための新しいカスタムのアクセス権を作成するとします。
まず、抽象クラス 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 クラスの抽象メソッドを実装した 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 に所有権があるキーストアタイプ名「JDK」です。
ポリシーファイルの各 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 (コンマ区切りの文字列リスト) CodebaseEntry -> codebase (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¥*"
ポリシーファイルとセキュリティプロパティファイルでは、プロパティの展開が可能です。プロパティの展開は、シェル内の変数の展開と似ています。ポリシーファイルまたはセキュリティプロパティファイルに次のような文字列がある場合、
この文字列は指定されたシステムプロパティの値まで展開されます。たとえば、以下では、${user.home} は user.home システムプロパティの値が使用できるように展開されます。
permission java.io.FilePermission "${user.home}", "read";
このプロパティの値が /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:¥jdk1.2 に設定されていても、次のように変換されます。
grant codeBase "file:/C:/jdk1.2/lib/ext/"
したがって、コードベース文字列では ${/} を使う必要はありません。また、使うべきではありません。プロパティの展開は、ポリシーファイルで二重引用符の使用が許可される位置ではどこでも発生します。これには、署名者、コードベース、ターゲット名、動作の各フィールドがあります。
プロパティの展開が許可されるかどうかは、セキュリティプロパティファイルの policy.expandProperties プロパティの値によって決まります。このプロパティの値が true (デフォルト) であれば、展開が許可されます。
注: 入れ子のプロパティは使用できません。たとえば次のように foo プロパティが home に設定されていても、このプロパティは機能しません。
"${user.${foo}}"
その理由は、プロパティの構文解析では入れ子のプロパティを認識できないからです。構文解析プログラムは、最初の ${ を探し、次に最初の }& を探してから、見つかった ${user.$foo} をプロパティとして解釈しようとします。しかし、このプロパティがない場合は解析に失敗します。また、grant エントリ、permission エントリ、keystore エントリでプロパティを展開できない場合、そのエントリは無視されます。たとえば、システムプロパティ foo が定義されていないときは、次の grant エントリ内の permission はすべて無視されます。
grant codeBase "${foo}" { permission ...; permission ...; };
次の場合は、permission Foo... エントリだけが無視されます。
grant { permission Foo "${foo}"; permission Bar; };
また次の場合は、keystore エントリが無視されます。
keystore "${foo}";
then the keystore entry is ignored.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/jdk/ と 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 は JDK がインストールされているディレクトリを示すシステムプロパティです。デフォルトでは、ユーザポリシーファイルは次の位置にあります。
{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」がセキュリティマネージャをインストールする場合は必要ありません。次のように、「==」の付いた形を使うと、ここで指定されたポリシーファイルだけがロードされ、他のポリシーファイルは無視されます 。
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 とアプレットビューアの両方のコマンドで)。このプロパティのデフォルト設定は true です。
Policy クラスの現在の設計は、すべての場合に適用できる包括的なものではありません。その点については考察が重ねられ、多くの場合に適したメソッドの呼び出しが確実にできるよう部分的な改良が進められています。それまでの間、デフォルトのポリシークラスを別のポリシークラスに置き替えることができます。ただし、置き換えるポリシークラスが抽象 Policy クラスのサブクラスであり、getPermissions
メソッド (および必要に応じて他のメソッド) を実装していることが条件です。デフォルトの Policy 実装は、policy.provider セキュリティプロパティ (セキュリティプロパティファイル内) の値を、目的の Poliocy 実装クラスの完全修飾形の名前に設定し直すことにより変更できます。セキュリティプロパティファイルは、次の名前のファイルです。
{java.home}/lib/security/java.security (Solaris) {java.home}¥lib¥security¥java.security (Windows)
ここで、{java.home} は JDK がインストールされているディレクトリを指します。プロパティは、ポリシークラスの名前を指します。デフォルトは次のとおりです。
policy.provider=sun.security.provider.PolicyFile
カスタマイズするには、このプロパティの値が別のクラスを指すように、次のように変更します。
policy.provider=com.mycom.MyPolicy
MyPolicy クラスは、java.security.Policy のサブクラスでなければなりません。このような、ポリシークラスのオーバーライドはあくまでも一時的な解決策であり、より包括的なポリシー API が設計されれば不要になります。
これは、新しい例外クラスで、java.lang.Exception のサブクラスです。これは、セキュリティおよびセキュリティパッケージ関連の例外を 2 種類にすることを意図して追加されました。
このような例外は、ある種のセキュリティ違反が検出された場合にだけスローされます。たとえば、あるコードがアクセス権のないファイルにアクセスしようとする場合に、この種の例外がスローされます。アプリケーション開発者は、任意でそれらの例外をキャッチできます。
これらの例外はセキュリティに関連したものですが、必要不可欠というわけではありません。たとえば、無効な鍵を渡すことはセキュリティ違反ではないと考えられますが、その鍵は開発者によりキャッチおよび処理される必要があります。RuntimeException のサブクラスである java.security パッケージ内に、現時点で 2 つの例外があります。下位互換性により、現時点ではこれらの例外は変更できません。将来この問題を再検討します。