このドキュメントでは、特権コードの意味と特権コードを使う目的について説明します。そのあとで API の使用方法を示し、特に次の点に注目します。
- 値を返す方法
- 型の安全性
- 例外処理
- リフレクション
JDK のインストールポリシーにより、特定のコードソースに基づくコードに対して許可されるアクセス権、つまりシステムリソースに対するアクセスの種類が決まります。CodeSource 型の「コードソース」は、基本的に、コードの場所 (URL) と、コードへの署名に使われている非公開鍵に対応する公開鍵を含む証明書の参照 (コードが署名されている場合) で、構成されています。
ポリシーは、Policy オブジェクトによって表されます。具体的には、
Policyクラス (java.securityパッケージ内) で定義されている抽象メソッドを実装したPolicyサブクラスによって表されます。Policy オブジェクトが使用するポリシー情報がどこに置かれるかは、Policy の実装によります。Policy のデフォルト実装では、ポリシー情報をポリシー構成ファイルから得ます。Policy のデフォルト実装とそこで読み取られるポリシーファイルで使用する構文については、「Policy のデフォルト実装とポリシーファイルの構文」を参照してください。Policy Tool を使って、構文に関する知識なしでポリシーファイルを作成する方法については、Policy Tool のドキュメント (Solaris 版) (Windows 版) を参照してください。
そのとき有効になっているセキュリティポリシーの決定に従って、CodeSource 自体と、その CodeSource に基づくコードに対して許可されているアクセス権は、「保護ドメイン」に包み込まれます。したがって、同じ鍵で署名された同じ URL にあるクラスは、同じドメインに入れられて、クラスは 1 つの保護ドメインだけに所属します。同じアクセス権を与えられていても、別のコードソースが基になっているクラスは、別のドメインに属します。
現在、JDK の一部として提供されているコードはすべてシステムコードと見なされて、固有のシステムドメインの中で実行されます。システムコードには、自動的にすべてのアクセス権が与えられます。
すべてのアプレットまたはアプリケーションは、それぞれのコードソースによって決まる適切なドメインの中で実行されます。アプレットまたはセキュリティマネージャの下で実行されるアプリケーションに、セキュリティ保護された動作 (ファイルの読み取りや書き込みなど) を許可するには、その特定の動作に対するアクセス権をアプレットやアプリケーションに与える必要があります。
つまり、リソースにアクセスしようとする場合は常に、その地点に至るまでに実行スレッドがたどってきたすべてのコードが、そのリソースアクセスのためのアクセス権を持っていなければなりません。ただし、スレッドの中に「特権」を与えられたコードがある場合は除きます。つまり、複数の呼び出し側のチェーンが存在する実行のスレッドで、アクセス制御の検査が行われる場合を考えます。保護ドメインの境界を横断する可能性のある複数のメソッドの呼び出しのような場合です。最後の呼び出し側が
AccessControllerのcheckPermissionメソッドを呼び出すとき、要求されたアクセスの許可または拒否を決定する基本的なアルゴリズムは、次のようになります。呼び出しチェーンのどれかの呼び出し側のコードに、要求されたアクセス権がない場合、AccessControlException がスローされます。ただし、このアクセス権を与えられているコードの呼び出し側が「特権付き」 (下記参照) に指定されていて、続いてこの呼び出し側から呼び出される部分 (直接または間接) がすべてこのアクセス権を持っている場合は、この例外はスローされません。
コードを「特権付き」に指定すると、信頼できるコードは、それを呼び出しているコードが直接利用できるものより多くのリソースに、一時的にアクセスできるようになります。ある種の状況では、このような機構が必要になります。たとえば、アプリケーションがフォントを含むファイルに直接アクセスすることを認められていない場合でも、ドキュメントを表示するシステムユーティリティはユーザの代わりにそのフォントを取得する必要があります。そのために、フォントを取得する間、システムユーティリティは特権付きになります。
以下のセクションでは、「特権」機能の使用方法について説明します。戻り値がなく、例外がスローされない場合
特権ブロック内から値を返す必要がない場合は、
doPrivilegedへの呼び出しは次のように記述できます。somemethod() { ...normal code here... AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // privileged code goes here, for example: System.loadLibrary("awt"); return null; // nothing to return } }); ...normal code here... }
AccessController.doPrivilegedメソッドは、java.security.PrivilegedAction型のオブジェクトを受け取り、そのrunメソッドを特権モードで呼び出します。非同期の例外によりdoPrivilegedの実行が中断される場合でも、runメソッドを実行したあとの特権の解除は、実装が保証します。
PrivilegedActionは、Object を返すrunという名前の 1 つのメソッドを含むインタフェースです。上の例は、匿名の内部クラスを使用するインタフェースの実装の作成を示しており、runメソッドの固定実装が提供されます。doPrivilegedを呼び出すと、PrivilegedAction の実装のインスタンスがメソッドに渡されます。doPrivilegedは、特権を有効にしたあとで、PrivilegedAction の実装からrunメソッドを呼び出し、runメソッドの戻り値をdoPrivilegedの戻り値として返します。上の例では戻り値は無視されています。「特権コード」を構成する実際の要素によっては、内部クラスの処理のために若干の変更が必要になる場合があります。たとえば、「特権コード」が例外をスローしたり局所変数にアクセスしたりする場合は、あとの節で説明するような変更が必要になります。
匿名の内部クラスを使わずに
doPrivilegedを呼び出すこともできます。somemethod() { ...normal code here... MyAction mya = new MyAction(); // become privileged: AccessController.doPrivileged(mya); ...normal code here... } class MyAction implements PrivilegedAction { public MyAction() {}; public Object run() { // privileged code goes here, for example: System.loadLibrary("awt"); return null; // nothing to return } }「特権」の構造は十分に注意して使い、特権コードをできる限り小さくするよう常に心がける必要があります。つまり、
runメソッドの中のコードは、特権付きで実行する必要のあるコードだけに制限し、より一般的な処理はrunメソッドの外側で行うようにします。また、doPrivilegedの呼び出しは、特権を有効にする必要のあるコードの中で行うようにします。セキュリティホールとなる危険があるため、それ自体がdoPrivilegedを呼び出すようなユーティリティクラスを作成してはいけません。上記の例で示したとおり、直接呼び出さなくても、PrivilegedActionクラスのためのユーティリティクラスを記述できます。値を返す場合
次は、値を返す必要がある場合の例です。
somemethod() { ...normal code here... String user = (String) AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return System.getProperty("user.name"); } } ); ...normal code here... }この方法では、
doPrivilegedから返される値を動的にキャストする必要があります。キャストの代わりに、finalの局所変数を使う方法があります。somemethod() { ...normal code here... final String user[] = {null}; AccessController.doPrivileged( new PrivilegedAction() { public Object run() { user[0] = System.getProperty("user.name"); return null; // still need this } } ); ...normal code here... }さらに、型を安全に扱う匿名ではないクラスを専用に作成する方法もあります。somemethod() { ...normal code here... GetPropertyAction gpa = new GetPropertyAction("user.name"); AccessController.doPrivileged(gpa); String user = gpa.getValue(); ...normal code here... } class GetPropertyAction implements PrivilegedAction { private String property; private String value; public GetPropertyAction(String prop) { property = prop;} public Object run() { value = System.getProperty(property); return value; } public String getValue() {return value;} }この例には型のキャストは含まれていませんが、
runメソッドからは値が返るので、必要に応じて、1 行に記述することもできます。somemethod() { ...normal code here... String user = (String) AccessController.doPrivileged( new GetPropertyAction("user.name")); ...normal code here... }局所変数にアクセスする場合
匿名の内部クラスを使う場合は、アクセスする局所変数をすべて
finalにする必要があります。次はその例です。somemethod() { ...normal code here... final String lib = "awt"; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // privileged code goes here, for example: System.loadLibrary(lib); return null; // nothing to return } }); ...normal code here... }変数
libを特権ブロックの内部で使う場合は、変数をfinalとして宣言する必要があります。詳細については、「内部クラス」の仕様を参照してください。繰り返し設定されるなどの理由で、既存の変数を
finalとして宣言できない場合は、doPrivilegedを呼び出す直前に新しいfinal変数を作成し、その変数を他の変数と等しいものとして設定します。次はその例です。somemethod() { ...normal code here... String lib; ... // lib gets set multiple times so we can't make it final ... // create a final String that we can use inside of the run method final String fLib = lib; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // privileged code goes here, for example: System.loadLibrary(fLib); return null; // nothing to return } }); ...normal code here... }例外の処理
runメソッドの中で実行する処理で、「チェック」例外 (メソッドのthrows節に列記されなければならない例外) がスローされる場合は、PrivilegedActionインタフェースではなく、PrivilegedExceptionActionインタフェースを使う必要があります。somemethod() throws FileNotFoundException { ...normal code here... try { FileInputStream fis = (FileInputStream) AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws FileNotFoundException { return new FileInputStream("someFile"); } } ); } catch (PrivilegedActionException e) { // e.getException() should be an instance of FileNotFoundException, // as only "checked" exceptions will be "wrapped" in a //PrivilegedActionException. throw (FileNotFoundException) e.getException(); } ...normal code here... }チェック例外が
runメソッドの実行中にスローされる場合は、上の例で示したように PrivilegedActionException の「ラッパー」例外に置いてからスローし、記述したコードを使ってキャッチします。リフレクション
この API とリフレクションの相互作用は、注意の必要な微妙な点です。doPrivileged()メソッドは、java.lang.reflect.Method.invoke()メソッドを使ってリフレクションとして呼び出すことができます。この場合、特権モードで与えられる特権は、Method.invoke()の特権ではなく、それによって呼び出されるリフレクションでないコードの特権です。このようにしないと、システム特権が誤って (または悪意を持って) ユーザコードに与えられてしまう危険があります。リフレクションを通して既存の API を使う場合にも、同様の要件が適用されます。
|
Copyright © 1997-98 Sun Microsystems, Inc. All Rights Reserved. コメントの送付先: java-security@java.sun.com |
Java ソフトウェア |