JDK 1.0以前のセキュリティ

サンドボックスと呼ばれる仕組みがブラウザに用意されました。Javaアプレットについてはサンドボックスと呼ばれるインタフェースを通さなければ、すべての機能が実行されないようになりました。

Javaアプレットが実行しようとする機能はすべてサンドボックスによってチェックされ、サンドボックスが持つ唯一のポリシー(判断基準)に従って、その実行の許可/不許可が決定され、実行を許可できない機能についてはSecurityExceptionが発行されます。

JDK 1.1のセキュリティ

JDK 1.0では多機能で実用的なアプレットを運用することが困難なため、JDK 1.1では電子署名付きアプレットという概念が導入されました。

アプレットのJARファイルに暗号化技術を使って電子署名を施したのが電子署名付きアプレットです。これにより、アプリケーション同様にあらゆる機能の実行を許可することが可能になりました。

Java 2のセキュリティ

Java 2ではセキュリティポリシーのconfigurationという機能が持ち込まれました。セキュリティポリシーというのは、様々なプログラムを実行するユーザがどのプログラムにどのリソース(ファイル、ネット)に対するどのようなアクセスを許可するのか、その方針(ポリシー)のことを指します。

JDK 1.1でサンドボックスのセキュリティポリシーを変更できなかったわけではなく、SecurityManagerというオブジェクトを自分で用意することで、任意のポリシーをサンドボックスに組み込むことが可能でした。しかし、SecurityManagerの実装は、セキュリティ自身に関する深い知識とセキュリティホールを作りこまない慎重な開発が要求されてしまうので、あまりお勧めできないという意味で馴染みの無い方法でした。

そこで、Java 2ではプログラムを書かずしてセキュリティポリシーを記述する手段を提供しようとしています。そうすることで、ポリシーの変更を簡単にできるようにするとの同時に、その記述を単純化することでセキュリティホールを作りこんでしまう可能性を小さくしようといるのです。さらにまた、アプレット以外のアプリケーションやクラスライブラリについても、無条件に信用するのでなく、ポリシーに従うようにデフォルトが切り替えられました。

Java 2セキュリティ機能の詳細

Java 2では、Java仮想マシン (Virtual Machine: VM) 上でインスタンス化されるクラスの各々について、次に示す項目をVM上に記述(設定)することができます。

をVM上に記述(設定)することができる。

もし、特に記述が無いクラスや電子署名されていないクラスがあった場合は、それらはサンドボックス内でインスタンス化される(つまりこれまでのアプレットと同じ扱いを受ける)。性格には、「誰が作ったクラスでもよい」という設定をすることもできる。そういう設定をされたクラスは、電子署名をさえていなくても実行できる。

ここで、このようなセキュリティポリシーの記述を実際に有効にするためには、次のような役割を果たすクラスが必要となる。

VMに設定されたポリシーは Policy オブジェクトによって読み込まれる。Policyクラス自身はabstractクラスであり、実体はVMパッケージごとに異なる実装(xxxPolicyクラス)となります。

Java 2に標準実装されているPolicyクラスは、特殊なフォーマットのテキストファイルにポリシーを記述するようになっています。もちろん他にもPolicyクラス中のポリシーをハードコーディングしてしまう方法やSerializeされたオブジェクトを読み込むPolicyクラス等、いろいろの実装方法が考えられます。(JDK以外のVMがどういうデザインをするのかによる)

ポリシーの個々のエントリは ProtectionDomain オブジェクトとして表現される。ProtectionDomain オブジェクトは、2つのオブジェクトを情報として持ちます。ひとつは CodeSource オブジェクトであり、どのURL先に置いてあって、誰が電子署名したクラスかを特定するものです。もうひとつは Permissions オブジェクトであり、どのリソースに対してのどのアクションかを特定するものです。ProtectionDomain オブジェクトに問い合わせることにより、あるクラスからの特定のメソッド呼び出しが許可されているかどうかをチェックすることができます。

あるクラスがどんなアクションを許されているのかを判断するクラスは AccessController クラスです。AccessControllerオブジェクトは、VMにただひとつ存在し、ただひとつのポリシー(Policyオブジェクト)を管理します。AccessControllerオブジェクトは、checkPermission() というメソッドを持ち、任意のメソッドが「自分自身が実行可能か」をチェックするために使います。(直接checkPermission()を呼ぶのではなく、SecurityManagerオブジェクトのインタフェースを通して呼び出す)

コアクラスのうち、セキュリティ上アクセスを制限すべきメソッドをも作らすがいくつかあります。これらのメソッドは様々なアプリケーションから呼び出されることになりますが、呼び出されるたびに自分自身が実行可能かをSecurityManagerオブジェクト経由でAccessControllerオブジェクトに問い合わせます。

VM側は適当なPolicyオブジェクトをAccessControllerオブジェクトに渡しておきます。アプリケーション側は個々のコアAPIの中から、個々のAPIを実行可能かをAccessControllerオブジェクトに問い合わせます。

CodeSource クラス
HTML文中に記述するのと同じ「codebase」と、そこに置かれたクラスの電子署名に対応した公開鍵を含んだもの。
Permission クラス
特定のリソースへの特定のアクションを表現したもの。
ProtectionDomain クラス
同じURLに置いてあるクラスと、それらに対するPermissionをまとめたもの。
Policy クラス
VMのユーザが記述したポリシーを、CodeSourceとPermissionのペア(すなわちProtectionDomainクラス)を並べたものして表現したもの。Policyオブジェクトは複数存在しえるが、ある時点ではそのうちのひとつしか有効ではない。
AccessController
VMが持っているPolicyに従って、あるアクセスが許可されているかどうかを判断する。

ProtectionDomain について

ProtectionDomain オブジェクトには次の性質があります。

要は、プログラム単位にパーミッションが決められているのではなく、クラス単位に決められているということである。ひとつのプログラムは普通いろいろなクラスを使って動作するので、ひとつのプログラムはその動作中にいろいろなパーミッションの下を渡り歩くことになります。なお、ProtectionDomainオブジェクトをアプリケーション側から更新することは普通ありません。

Permission について

PermissionオブジェクトはAccessControllerへの問い合わせにも使用される。その場合は自分でPermissionオブジェクトをインスタンス化します。

実際のPermissionクラスはターゲットのリソースごとにサブクラスかされています。

new FilePermission("/tmp/abc", "read");

/tmp/abc というファイルの読み込み可能を示しています。

PermissionCollection クラスを使うと、複数のPermissionを1グループとして扱える。Permissionのグループをグループ化する Permissions というクラスもあります。PermissionCollection(例えばFilePermissonだけ)を集めたホモジニアスなPermissionを集めたもの(例えばFilePermissionとかSocketPermissionが混在したもの)となる。

Permissionを動的に生成する(ランタイムに生成する)ことも可能。

アクションの指定なしでターゲットの名前だけが参照可能(単機能?)なPermissionとして BasicPermission クラスがあります。このクラスでは、ターゲットの名前としてパッケージ名と同様な「.(ピリオド)」と「*」で記述した文字列を使うことができるのが特徴です。

このBasicPermissionはファイルやサーバといった特定のターゲットを前提にせずに単に文字列としてのターゲット名をサポートしているので、通常はこれをサブクラス化し、オリジナルなPermissionとして各種アクションを追加したものを作る。このときは、ターゲット名に「*」を使った表現をそのまま使うことができるので楽である。

デフォルトですべての機能を許可する AllPermission クラスもあります。

各Permissionは「implies() メソッド」を実装し、あるPermissionに対して、別のPermissionが包含されるかどうか(前者によって後者が自動的に許可となるかどうか)をチェックできるようにしなければならない。

しかし、別のタイプのPermissionを含んでしまうようなPermissionもありうるので、正確に(セキュリティホールがないように)impliesメソッドを実装するのは困難でしょう。

Permissionクラスのサブクラスをオリジナルに開発することも可能です。これは、ある特殊なVM環境でセキュリティを保ちたいものがあるときに必要となります。

SecurityManager の扱い

基本的にはSecurityManagerという概念はJava 2から使わないことになっています。代わりにPolicyオブジェクトを使えばよいのです。しかし、SecurityManagerオブジェクトを使いたいアプリケーションやAPIも存在しているため、互換性を取るためにSecurityManagerも残されています。

代表例はRMIです。RMIは「RMISecurityManager」をセットしていないアプリケーションに対して機能しないようデザインしています。

基本的にはSecurityManagerはコアAPIからPermissionのチェックを行うためのインタフェースに過ぎず、問い合わせをそのままAccessControllerに中継するだけであるが、RMISecurityManagerのようにAccessControllerが関与していないところで、ハードこーデングされたポリシーを持っているのもありうるということです。

AccessControllerの判定処理

AccessControllerオブジェクトは、一つだけ持っているPolicyオブジェクトとPermissionチェックを要求してきたクラス(&メソッド)とから、その機能の実行許可(不許可)を判断し、もし不許可の場合は、AccessControllException を発生させます。

しかし、この判断は、そう簡単ではありません。個々のコアAPIのメソッドを呼び出したのが誰(どのクラス)なのかによって判断結果は違ってくるからである。

実際、AccessControllerオブジェクトは、対象となるメソッドを呼び出したクラス(&メソッド)を逆順にたどってゆき、そのすべてのクラスが実行許可されている場合にだけ、最終的な「許可」の結論を出すのである。

ただし、その逆順チェックを省略する方法があります。すなわち、「どんなクラスから呼ばれたってセキュリティを自分自身で確保できる」と主張するクラスについては、そのクラスから先のチェックを省略可能としているのである。そのためには、このクラスの中から AccessController.beginPrivileged() というメソッドを呼び出せばよい。

そうすると、AccessControllerはPermissionチェック時にそのクラスまで呼び出し下を辿っていった時点で結論を出してくれるようになります。

Signed Object

任意のオブジェクトに電子署名を施すAPIも用意されました。特にSerializeされたオブジェクトの交換などに効果があります。

他人(他クラス)のPermissionのチェック

サーバプログラムなどによっては、あるサービスのリクエストを出したクライアントに対して、そのサービスを実行してよいかどうかを判断しなければならない場合があります。つまり、サーバ自身のクラスは実行可能な機能であっても、怪しいクライアントからのリクエストに従って実行してしまうと、危険な場合があるということです。そこで、AccessControllContext というオブジェクトが用意されました。これは、他人(他クラス)が持つセキュリティ情報を受け取って、代わりにPermissionのチェックを実行することを可能にします。

クライアントはサーバに対し、自分のAccessControlContextオブジェクトを私、サーバそのオブジェクトのcheckPermissionメソッドを使って、クライアントが特定の機能を実行する権利があるかどうかをチェックできます。

この機能は、スレッドの生成の時には強制的に実行されます。すなわち、ある子スレッドが生成され、そのスレッドがコアAPIを使うときには親スレッドのAccessContolContextも必ずチェックするように作られている。

これにより、怪しい親スレッドから生成された子スレッドがうっかり危険な処理を実行してしまうことを防ぐことができる。

といっても、クライアントがAccessControlContextを渡してくれない場合もありうる。そういうときは、そのクライアントがアクセスしたがっているリソースを GuardedObject にしてクライアントに渡してやればよい。このGuardedObjectの中には、サーバが判断したPermissionが含まれており、いざクライアントがそのリソースにアクセスしようとすると、Permissionに引っかかるようになっている。

さらに、アプレットのビルトイン機能以外からも電子署名付きコードのチェックが行える(そして、その結果に応じて動作を変える)ことができるようになりました。