特権ブロックのための API


概要

このドキュメントでは、特権コードの意味と特権コードを使う目的について説明します。そのあとで、次の点に特に注目しながら API の使用方法を示します。

背景: 特権付きコードを使う意味

SDK のインストールポリシーにより、特定のコードソースに基づくコードに対して許可されるアクセス権、つまりシステムリソースに対するアクセスの種類が決まります。CodeSource 型の「コードソース」は、基本的に、コードの場所 (URL) と、コードへの署名に使われている非公開鍵に対応する公開鍵を含む証明書の参照 (コードが署名されている場合) で、構成されています。

ポリシーは、Policy オブジェクトによって表されます。具体的には、Policy クラス (java.security パッケージ内) で定義されている abstract メソッドを実装した Policy サブクラスによって表されます。

Policy オブジェクトが使用するポリシー情報がどこに置かれるかは、Policy の実装によります。Policy のリファレンス実装では、ポリシー情報をポリシー構成ファイルから得ます。デフォルトの Policy のリファレンス実装とそこで読み取られるポリシーファイルで使用する構文については、「Policy のデフォルト実装とポリシーファイルの構文」を参照してください。Policy Tool を使った (構文を知る必要のない) ポリシーファイルの作成方法の詳細については、Policy Tool のドキュメント (Solaris 用) (Windows 用) を参照してください。

そのとき有効になっているセキュリティーポリシーの決定に従って、CodeSource 自体と、その CodeSource に基づくコードに対して付与されているアクセス権は、「保護ドメイン」に包み込まれます。したがって、同じ鍵で署名された同じ URL にあるクラスは、同じドメインに入れられて、クラスは 1 つの保護ドメインだけに所属します。同じアクセス権を与えられていても、別のコードソースが基になっているクラスは、別のドメインに属します。

現在、SDK の一部として提供されているコードはすべてシステムコードと見なされて、固有のシステムドメインの中で実行されます。システムコードには、自動的にすべてのアクセス権が与えられます。

アプレットまたはアプリケーションは、それぞれのコードソースによって決められるドメイン内で動作します。アプレットまたはセキュリティーマネージャーの下で実行されるアプリケーションに、セキュリティー保護された動作 (ファイルの読み取りや書き込みなど) を許可するには、その特定の動作に対するアクセス権をアプレットやアプリケーションに与える必要があります。

つまり、リソースにアクセスしようとする場合は常に、その地点に至るまでに実行スレッドがたどってきたすべてのコードが、そのリソースアクセスのためのアクセス権を持っていなければなりません。ただし、スレッドの中に「特権」を与えられたコードがある場合は除きます。つまり、複数の呼び出し側のチェーンが存在する実行のスレッドで、アクセス制御のチェックが行われる場合を考えます。保護ドメインの境界を横断する可能性のある複数のメソッドの呼び出しのような場合です。最後の呼び出し側が AccessControllercheckPermission メソッドを呼び出すとき、要求されたアクセスの許可または拒否を決定する基本的なアルゴリズムは、次のようになります。

呼び出しチェーンのいずれかの呼び出し側のコードに、要求されたアクセス権がない場合、AccessControlException がスローされます。ただし、このアクセス権を与えられているコードの呼び出し側が「特権付き」(下記参照) に指定されていて、続いてこの呼び出し側から呼び出される部分 (直接または間接) がすべてこのアクセス権を持っている場合は、この例外はスローされません。

コードを「特権付き」に指定すると、信頼できるコードは、それを呼び出しているコードが直接利用できるものより多くのリソースに、一時的にアクセスできるようになります。これは、次のような場合に必要になります。たとえば、あるアプリケーションがフォントが入ったファイルへの直接のアクセスを許可されていないときに、ドキュメントを表示するシステムユーティリティーがユーザーに代わってそのフォントを取得する必要がある場合などです。そのために、フォントを取得する間、システムユーティリティーは特権付きになります。

doPrivileged API の使用

以降のセクションでは、「特権」機能の使用方法について説明します。

戻り値がなく、例外がスローされない場合

特権ブロック内から値を返す必要がない場合は、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 というメソッドを持つインタフェースです。上の例は、匿名の内部クラスを使用するインタフェースの実装の作成を示しており、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 © 1993, 2013, Oracle and/or its affiliates. All rights reserved.