java.security.AccessController クラスはJavaのセキュリティ機能の一部であり、その役割はコードにセキュリティポリシーを適用することにある。このクラスの static メソッドである doPrivileged() は、より緩いセキュリティポリシーに基づいてコードブロックを実行する。doPrivileged() メソッドから呼び出されるコールチェーンでは、パーミッションのチェックは行われなくなる。
したがって、doPrivileged() を呼び出すすべてのメソッドは、doPrivileged() に実行させるコードブロックに対し、メソッドのセキュリティポリシーを強制する責任がある、ということを前提に設計されなくてはならない。同様に、doPrivileged() メソッド内のコードは、センシティブな情報や権限(capability)を漏えいすることがあってはならない。
例として、あるウェブアプリケーションにおいて、サービスで使用するパスワードファイルを管理し、かつ信頼できないコードも実行しなくてはならない場合について考えてみよう。そのようなアプリケーションでは、信頼できないすべてのコードはもちろん、コードの大部分が、パスワードファイルにアクセスできないようにするセキュリティポリシーを強制するであろう。また、パスワードの追加と変更を行う機能を提供しなくてはならないため、doPrivileged()メソッドを呼び出し、信頼できないコードが一時的にパスワードファイルにアクセスできるようにするかもしれない。その場合、特権を持つブロックは、信頼できないコードがパスワード情報にアクセスできないようにすべきである。
違反コード
以下の違反コード例では、openPasswordFile() メソッドの中で doPrivileged() メソッドを呼び出している。openPasswordFile() メソッドは特権を持ち、パスワードファイルを指す FileInputStream を返す。このメソッドは public 宣言されているため、信頼できないコードから呼び出されてしまうかもしれない。
public class PasswordManager { public static void changePassword() throws FileNotFoundException { FileInputStream fin = openPasswordFile(); // 変更前のパスワードとパスワードファイル中のパスワードが同じかチェックし、パスワードを変更 // その後、パスワードファイルをクローズ } public static FileInputStream openPasswordFile() throws FileNotFoundException { final String password_file = "password"; FileInputStream fin = null; try { fin = AccessController.doPrivileged( new PrivilegedExceptionAction<FileInputStream>() { public FileInputStream run() throws FileNotFoundException { // センシティブな処理、特権ブロックの外で実行してはならない FileInputStream in = new FileInputStream(password_file); return in; } }); } catch (PrivilegedActionException x) { Exception cause = x.getException(); if (cause instanceof FileNotFoundException) { throw (FileNotFoundException) cause; } else { throw new Error("Unexpected exception type", cause); } } return fin; } }
適合コード
一般に、特権ブロックを抱えるメソッドが信頼境界の外にフィールド(たとえばオブジェクトへの参照)を公開すると、信頼できないコードがそのようなプログラムを攻撃するのは容易になる。
以下の適合コードでは、openPasswordFile() を private 宣言することで脆弱性の脅威を低減している。信頼できないコードは changePassword() を呼び出すことはできても、openPasswordFile() メソッドを直接呼び出すことはできない。
public class PasswordManager { public static void changePassword() throws FileNotFoundException { // ... } private static FileInputStream openPasswordFile() throws FileNotFoundException { // ... } }
適合コード (例外を抑制する)
前述の2つのコード例はどちらも、パスワードファイルが見つからない場合に FileNotFoundException をスローする。パスワードファイルの存在自体をセンシティブな情報であると考えるなら、信頼できるコードの外にこの例外をスローしてはならない。
以下の適合コードでは例外を抑制し、ファイルが存在しないことを単一の null 値を含む配列として表現している。PrivilegedExceptionAction の代わりに、より単純な PrivilegedAction クラスを使用し、doPrivileged() ブロックの外に例外が伝播するのを防いでいる。特権を持って処理を行うメソッドが返り値を返さないのであれば、返り値の型を Void にするのがよい。
class PasswordManager { public static void changePassword() { FileInputStream fin = openPasswordFile(); if (fin == null) { // パスワードファイルが存在しない。エラー処理。 } // 変更前のパスワードとパスワードファイル中のパスワードが同じかチェックし、パスワードを変更 } private static FileInputStream openPasswordFile() { final String password_file = "password"; final FileInputStream fin[] = { null }; AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { try { // センシティブな動作であり、 // doPrivileged() ブロックの外で実行してはならない fin[0] = new FileInputStream(password_file); } catch (FileNotFoundException x) { // ハンドラに通知する } return null; } }); return fin[0]; } }
リスク評価
doPrivileged ブロックの中からセンシティブなリソースへの参照を返すと、カプセル化による制限が無効になり、権限を外部に漏えいする恐れがある。特権コードを直接呼び出し、センシティブなリソースやフィールドへの参照を取得することができる呼出し元は、それらの要素を不正に変更することができる。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
SEC00-J | 中 | 高 | 高 | P6 | L2 |
自動検出
どの情報がセンシティブであるかを特定するには、プログラマの情報提供が必要となる。完全に自動的にセンシティブな情報を特定することは、現在の技術では不可能である。
どの情報がセンシティブであるか指定されているならば、doPrivileged() ブロックに対するエスケープ分析(escape analysis)によって、センシティブな情報がブロック外に漏えいしないことを証明することができる。また、スレッドロール分析(thread-role analysis)に似た手法を使い、doPrivileged ブロックから呼び出してよいメソッドと呼び出してはいけないメソッドを特定することも可能である。
関連ガイドライン
MITRE CWE | CWE-266. Incorrect privilege assignment |
CWE-272. Least privilege violation | |
Secure Coding Guidelines for the Java Programming Language, Version 3.0 | Guideline 6-2. Safely invoke java.security.AccessController.doPrivileged() |
参考文献
[API 2006] | Method doPrivileged() |
[Gong 2003] | Sections 6.4, AccessController, and 9.5, Privileged Code |
翻訳元
これは以下のページを翻訳したものです。
SEC00-J. Do not allow privileged blocks to leak sensitive information across a trust boundary (revision 100)