時代遅れなアプレットを動かすためのポリシー設定格闘記


Java11がリリースされてアプレットが使えない時代が到来しているのに、未だにJavaアプレットで新しい機能を実現してほしいという需要がある。アプレットを使いたがる理由は様々だが、「JavaScriptからネイティブな処理を実行したいから」というのがほとんどだ。
そんなときに、いつもセキュリティ関係の環境設定にハマってしまうのは自分だけではないはず。本当はアプレットなんて使いたくないのに、やむなく妥協して使ってみた挙句、環境設定で時間を取られたときのストレスは半端ない。
そんな経験は一度で十分なので、今後そんな目に合わないよう、ここに出会ったハマりポイントをまとめておく。

java.policyの設定ミス

JavaScriptではできないネイティブ処理も、アプレットならできる。そう思っている人は要注意だ。たとえアプレットでも何の制限もなしに実行できるわけではないので、ちゃんと$JRE_HOME/lib/security/java.policyにセキュリティ上の例外設定をしなければならない。
これをしないと、アプレットからネイティブな処理(ファイル書き出しなど)を実行しようとしたときに実行時例外になる。

とりあえず動作させたいときは、全エントリに対して全権限java.security.AllPermission;を与えればいい

grant {
    permission java.security.AllPermission;
}

codeBaseで制限

特定のエントリに対してのみ設定したい場合、codeBaseを記述する。ローカルのドライブ上に配置するのか、Webからダウンロードして実行するのかにより指定が変わる。記述詳細はJavaの公式ドキュメントを参照するのがいい。

permissionで制限

特定の権限だけを与えたい場合、permission java.security.〜クラス名〜;を指定する。記述詳細はJavaの公式ドキュメントを参照するのがいい。

java.securityでjava.policyの読み込み漏れ

java.policyを正しく記述しているのに設定が反映されないなら、そもそもjava.policyが読みこまれているかを確認する。java.policyは複数のファイルに定義を分割し、読み込まれる順番で設定が上書きされるため、読み込み順はとても重要だ。
$JRE_HOME/lib/security/java.securityjava.policyを読み込むpolicy.url.n指定があるので、ここを確認する。デフォルトでは以下のようになっているだろう。

policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

${java.home}${user.home}についてはアプレット起動後に「Javaコンソール」を出してプロパティ確認したり、確認用のプログラムでも作って確認すればいい。

System.out.println(System.getProperty("user.home"));

その他policy.url.nの詳細については公式ドキュメント参照。
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/PolicyFiles.html#DefaultLocs

特権実行のためのコード

java.policyを正しく設定しているのにパーミッション例外になる場合は、そもそもJavaアプレットが特権実行を考慮した実装になっているかを確認する。セキュリティ例外になる処理は、特権ブロックのためのAPIjava.security.AccessController#doPrivilegedを利用していないと実行できない。

よくあるケースとして、テスト環境ではとりあえず動作させたい設定(grantでcodebase指定なくAllPermission指定)にしていたのが、本番環境では動かないと嘆くパターンだ。java.policyが変わっただけで動かなくなったように感じるため「java.policyが間違っている」と勘違いしてしまうが、実はアプレットの実装が悪いのだ。

public class SampleApplet extends Applet {
    public void hoge() {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                // 権限が必要な処理
                //new Robot().createScreenCaptureなど
                return null;
            }
        });
    }
}

特権ブロックについての公式ドキュメントは↓↓
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/doprivileged.html