Java SE 6 と Java SE 7 では、特権で動作するコードは、AccessController の仕組みを使用するか、ユーザが信頼する所有者(あるいは提供者)によって署名されていなければならない。特権で動作するコードが直接あるいは間接的に他のパッケージのコードを呼び出すようになっている場合、攻撃者は、特権で動作するコードから悪意あるコードを呼び出させることができる。自分自身は特権を必要としないが特権を持って動作する他のコードを使用するようなコードが、信頼する JAR ファイルには含まれていることが多い。そのようなコードをセキュリティ上センシティブなコード("security-sensitive code")と呼ぶ。もしセキュリティ上センシティブなコードから悪意あるコードを呼び出させることができれば、攻撃者は間接的に、プログラムに間違った動作をさせることが可能になる。このような攻撃手法を 「つぎはぎ攻撃(mix-and-match attack)」と呼ぶ。
通常、信頼できないコードを実行すると権限は失われる。Java セキュリティモデルは、信頼するメソッドが信頼できないメソッドを呼び出すと、権限を無効にする。信頼できるコードが信頼できないコードを呼び出し、信頼できないコードがセキュリティポリシーで禁止されている操作を実行しようとしても、Java セキュリティモデルが、その操作を許可しない。しかし、権限を持つコードは、信頼できないコンテナに含まれているクラスを使用し、権限を必要としない操作のみを実行する可能性がある。攻撃者が、信頼できないコンテナに含まれるクラスを悪意ある実装に置き換えることができれば、信頼できるコードは間違った処理結果を得て、攻撃者が思う通りの動作をさせられる危険がある。
Java API には以下のような記述がある[EMA 2011]。
JAR ファイル内のパッケージをシールした場合、そのパッケージ内で定義されているすべてのクラスは、同一の JAR ファイルが元になっていなければならない。そうでない場合は、SecurityException がスローされる。
JAR ファイルをシールすることにより、権限を必要とするコードを1つにまとめるという制約を自動的に課すことになる。さらに、クラスとそのメンバのアクセス範囲を最小限にしておくことも重要である。
違反コード (特権を持って動作するコード)
以下の違反コード例では、doPrivileged() ブロックの中で、信頼できない他の JAR ファイルに含まれているクラスで定義されているメソッドを呼び出している。
package trusted; import untrusted.RetValue; public class MixMatch { private void privilegedMethod() throws IOException { try { AccessController.doPrivileged( new PrivilegedExceptionAction<FileInputStream>() { public FileInputStream run() throws FileNotFoundException { final FileInputStream fis = new FileInputStream("file.txt"); try { RetValue rt = new RetValue(); if (rt.getValue() == 1) { // センシティブなファイルを使った処理 } } finally { fis.close(); } return fis; } } ); } catch (PrivilegedActionException e) { // 処理をハンドラに移し、ログをとる } } public static void main(String[] args) throws IOException { MixMatch mm = new MixMatch(); mm.privilegedMethod(); } } // 別の JAR ファイル package untrusted; class RetValue { public int getValue() { return 1; } }
攻撃者は、特権で動作するコードが間違った返り値を使うように、RetValue クラスの実装を与えることができる。MixMatch クラスには信頼できる署名されたコードしか含まれていないが、攻撃者が(信頼できない) RetValue クラスの実装を含む署名付き JAR ファイルを与えることによって、このように動作させることができる。
このコード例は「SEC01-J. 汚染された変数を特権ブロックの中で使わない」に違反しているように見えるが、実際には違反していない。doPrivileged() ブロックの中に汚染される可能性のあるコードが含まれているということが問題である。
違反コード (セキュリティ上センシティブなコード)
以下の違反コード例は前のコード例を改善したものである。RetValue クラスの使用を doPrivileged() ブロックの外に出している。
package trusted; import untrusted.RetValue; public class MixMatch { private void privilegedMethod() throws IOException { try { final FileInputStream fis = AccessController.doPrivileged( new PrivilegedExceptionAction<FileInputStream>() { public FileInputStream run() throws FileNotFoundException { return new FileInputStream("file.txt"); } } ); try { RetValue rt = new RetValue(); if (rt.getValue() == 1) { // センシティブなファイルを使った処理 } } finally { fis.close(); } } catch (PrivilegedActionException e) { // 処理をハンドラに移し、ログをとる } } public static void main(String[] args) throws IOException { MixMatch mm = new MixMatch(); mm.privilegedMethod(); } } // 別の JAR ファイル package untrusted; class RetValue { public int getValue() { return 1; } }
RetValue クラスは doPrivileged() ブロックの外でしか使われていないが、RetValue.getValue() の動作は、doPrivileged() ブロックの中でオープンされたファイルを操作するセキュリティ上センシティブなコードの動作にも影響する。そのため、依然として攻撃者は、RetValue の実装を使ってセキュリティ上センシティブなコードを攻撃できる。
適合コード
以下の適合コードでは、セキュリティ上センシティブなコードを1つのパッケージにまとめ、それを同一の JAR ファイルに入れている。また、getValue() メソッドのアクセス範囲をパッケージプライベートに制限している。攻撃者が悪意あるクラスを挿入することを防ぐには、パッケージをシールする必要がある。
package trusted; public class MixMatch { // ... } // 同一の署名およびシールされた JAR ファイル package trusted; class RetValue { int getValue() { return 1; } }
パッケージをシールするには、以下のように、JAR ファイルのマニフェストファイルヘッダに sealed 属性を追加する。
Name: trusted/ // パッケージ名 Sealed: true // シール属性
例外
ENV01-EX0: 以下の条件をすべて満たす場合のみ、特権で動作するコードとそれに関連するセキュリティ上センシティブなコードのグループ(以下、「グループ」と呼ぶ)の集合を、別々のシールされたパッケージに分散させたり、さらには異なる JAR ファイルに分散させてもよい。
- あるグループのコードは他のどのグループのコードにも動的にも静的にも依存していない。これはつまり、あるグループのコードが他のグループのコードを直接呼び出したり、間接的に呼び出したりすることができない、ということである。
- 1つのグループの中のすべてのコードは1つ以上のシールされたパッケージに含まれる。
- 1つのグループの中のすべてのコードはシールされた1つの JAR ファイルに含まれる。
リスク評価
特権で動作するコードを1つのパッケージにまとめてシールしておかなければ、つぎはぎ攻撃を受ける危険がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
ENV01-J | 高 | 中 | 中 | P12 | L1 |
自動検出
権限を持つコードやセンシティブなコードを検出するには、プログラマの情報提供が必要である。特権を持つコードがどれであるかが分かれば、そこから呼び出されるコードの全体を調べ、それが1つのパッケージの中に収まっているかどうかをチェックすることは可能である。また、そのパッケージがシールされているかどうかをチェックすることも可能である。
関連ガイドライン
MITRE CWE | CWE-349. Acceptance of extraneous untrusted data with trusted data |
参考文献
[EMA 2011] | Optional Package Sealing |
[McGraw 1999] | Rule 7, If you must sign your code, put it all in one archive file |
[Ware 2008] |
翻訳元
これは以下のページを翻訳したものです。
ENV01-J. Place all security-sensitive code in a single JAR and sign and seal it (revision 89)