センシティブな処理はセキュリティマネージャによるチェックで保護しなければならない。
違反コード
以下の違反コード例では、Hashtable クラスのインスタンスを生成しており、ハッシュテーブルのエントリを削除する removeEntry() メソッドを定義している。ハッシュテーブルにはセンシティブな情報が含まれているかもしれないため、removeEntry() メソッドはセンシティブな処理であると考えられる。しかし、このメソッドは public 宣言されているが final 宣言はされておらず、悪意ある攻撃者が自由に呼び出すことができる状態にある。
class SensitiveHash { Hashtable<Integer,String> ht = new Hashtable<Integer,String>(); public void removeEntry(Object key) { ht.remove(key); } }
適合コード
以下の適合コードでは、Hashtable インスタンスからエントリが不正に削除されることを防ぐため、セキュリティチェックを行っている。removeEntry() メソッドの呼出し元が java.security.SecurityPermission の removeKeyPermission を許可されていなければ、SecurityException 例外がスローされる。
class SensitiveHash { Hashtable<Integer,String> ht = new Hashtable<Integer,String>(); void removeEntry(Object key) { check("removeKeyPermission"); ht.remove(key); } private void check(String directive) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkSecurityAccess(directive); } } }
SecurityManager.checkSecurityAccess() メソッドを使うことで、特定のパーミッションで制御されている操作が許可されているかどうかを確認することができる。
違反コード (check*())
以下の違反コード例では、SecurityManager.checkRead() メソッドを使って、ファイル schema.dtd が読取り可能かどうかを確認している。しかし、check*() メソッドは細かいアクセス制御を行えない。たとえば、拡張子 dtd のついたファイルの読取りを許可し、それ以外のすべてのファイルの読取りを禁止する、というようなセキュリティポリシーは実現できない。JDK の一部でないコードは、check*() メソッドをオーバーライドしてはならない。JDK の Java ライブラリ実装がセンシティブな処理の保護のためにこれらのメソッド群を使っているからである。
SecurityManager sm = System.getSecurityManager(); if (sm != null) { // ファイル読取りが許可されているかどうかをチェックする sm.checkRead("/local/schema.dtd"); }
適合コード (checkPermission())
Java SE 1.2 からは、SecurityManager クラスに2つのメソッド checkPermission(Permission perm) と checkPermission(Permission perm, Object context) が追加された。変更が行われた理由は以下の点にある。
- メソッドの名称からパーミッション名をハードコードする必要をなくす。
- Java 実行環境で行われる複雑なアルゴリズムやコードを checkPermission() メソッドにカプセル化する。
- Permission クラスを拡張することで追加のパーミッションを定義できるようにする。
単一引数の checkPermission() メソッドは、現在実行中のスレッドのコンテキストで、パーミッションを確認する。コンテキストがローカルポリシーファイルで定義されたパーミッションを持つならば、リソースへのアクセスは許可され、そうでなければ SecurityException 例外がスローされる。
この適合コードでは、単一引数の checkPermission() メソッドを使って、local ディレクトリにある拡張子 dtd のついたファイルの読取り許可を確認している。このようなパーミッションを、独自に設定したパーミッション DTDPermission で表現している。これにより、java.io.FilePermission の read アクションの許可に加えて、DTD ファイルに対するアクセス制限を実装することができる。
SecurityManager sm = System.getSecurityManager(); if (sm != null) { // ファイル読取りが許可されているかどうかをチェックする DTDPermission perm = new DTDPermission("/local/", "readDTD"); sm.checkPermission(perm); }
適合コード (マルチスレッド)
あるコンテキストにセキュリティチェックを行うコードがあり、実際のチェックは別のコンテキストで行わなければならない場合がある(たとえばワーカースレッドとその他のスレッド)。このような場合には2引数の checkPermission() メソッドを使う。このメソッドは context 引数として AccessControlContext のインスタンスを受け取る。許可されるパーミッションは、context 引数で渡されたコンテキストのもののみであり、2つのコンテキストの共通部分ではない。
1引数および2引数のcheckPermission() メソッドはどちらも、内部で java.security.AccesController.checkPermision(Permission perm) メソッドを呼び出している。このメソッドを直接呼び出すと、現在実行中のコンテキストでパーミッションの確認が行われるため、2つの引数を取るセキュリティマネージャの checkPermission() メソッドの代りにはならない。
異なるコンテキストでセキュリティチェックを行う場合、もっと明解なアプローチとして java.security.AccessController.getContext() メソッドを使う方法がある。このメソッドを使うと、現在実行中のコンテキストの スナップショット である AccessControlContext オブジェクトを得ることができ、そのコンテキストにおけるセキュリティチェックを行うことができる。AccessControlContext クラスはコンテキストをカプセル化した checkPermission() メソッドを持つため、実行コンテキストを引数として受け取る必要はない。そのため、以下のコードに示すように、セキュリティチェックを後で行うことができる。
// 必要なコンテキストのスナップショットをとり、acc に代入して別のコンテキストに渡す AccessControlContext acc = AccessController.getContext(); // 別のコンテキストで acc を受け取り、acc のコンテキストで checkPermission() を呼び出す acc.checkPermission(perm);
リスク評価
センシティブな処理を行うコードでは、セキュリティチェックを確実に行わないと、センシティブなデータが不正にアクセスされる危険がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
SEC04-J | 高 | 中 | 中 | P12 | L1 |
自動検出
コードのどの部分がセンシティブな処理であるかを判別するためには、プログラマの情報提供が必要であり、この判別を完全に自動的に行うのは現状の技術では不可能である。
どの部分がセンシティブな処理なのか、そしてそれに対してどのようなセキュリティチェックを行うべきかといった情報が与えられていれば、センシティブな処理が、一定のセキュリティチェックが実施されたコンテキストでのみ行われていることを、解析ツールで検証することは可能である。
参考文献
[API 2006] |
翻訳元
これは以下のページを翻訳したものです。
SEC04-J. Protect sensitive operations with security manager checks (revision 56)