JPCERT コーディネーションセンター

ENV05-J. 遠隔から監視できる状態のままアプリケーションを実運用しない

Java では、実行中の Java プログラムの様子を外部のプログラムから監視するための API が提供されている。これらの API は、異なるホスト上のプログラムから監視できるようにする機能も提供する。このような機能は、デバッグや性能改善を行うには便利であるが、実運用環境で使うと、攻撃者が JVM に接続し、プログラムの動作や(センシティブである可能性もある)データを覗き見る危険がある。場合によっては、プログラムの動作を乗っ取る危険さえある。そのため、Java プログラムを実運用環境で実行するときには、遠隔監視機能を無効にしなければならない。

JVM Tool Interface (JVMTI)

Java 5 では、JVM Tool Interface (JVMTI) が導入され[Sun 04d]、それまでの JVM Profiler Interface (JVMPI) と JVM Debug Interface (JVMDI) は非推奨(deprecated) とされた。

JVMTI は、実行中の JVM の状態を知るための様々な機能を提供している。さらに、実行中の Java プログラムを監視したり、変更したりする機能も備えている。これらは低レベルの機能であるため、Java Native Interface (JNI) と C 言語によるプログラミングが要求される。しかし、これらの機能を使うことで、通常アクセスできないフィールドへのアクセスも可能になることに注意が必要である。さらに、実行中の Java プログラムの動作を変更することさえも可能である(たとえば、スレッドを一時中断させたり停止させるなど)。また、JVMTI プロファイリングツールを使えばスレッドの実行にかかった時間を計測することができるため、タイミング攻撃に悪用することもできる。

JVMTI は、実行中の JVM と通信するエージェントを使って動作する。これらのエージェントは JVM 起動時にロードされなければならない。通常は、コマンドラインオプション -agentlib:、あるいは -agentpath: のどちらかで指定される。また、環境変数でエージェントを指定することも可能であるが、セキュリティ上問題であれば、この機能は無効にできる。JVMTI は常に有効になっており、JVMTI エージェントはデフォルトのセキュリティマネージャの元で、何のパーミッションも持たずに動作することができる。

Java Platform Debugger Architecture (JPDA)

Java Platform Debugger Architecture (JPDA) は JVMTI の上に構築されており、動作中の Java システムをデバッグするための高レベルの機能を提供する[JPDA 2004]。

JPDA の機能は「SEC05-J. リフレクションを使ってクラス、メソッド、フィールドのアクセス範囲を広げない」で説明したリフレクション API に似ている。JPDA を使うと、フィールドや配列の値を取得したり設定したりできる。アクセス制御は働かず、JPDA を通じて遠隔のプロセスから private フィールドに値を設定することもできる。

デフォルトのセキュリティマネージャの元でデバッグを行うには、様々なパーミッションを与える必要がある。以下のポリシーファイルは、デフォルトのセキュリティマネージャの元で JPDA の Trace デモを実行するために使われるものである。

grant {
  permission java.io.FilePermission "traceoutput.txt", "read,write";
  permission java.io.FilePermission "C:/Program Files/Java/jdk1.5.0_04/lib/tools.jar", "read";
  permission java.io.FilePermission "C:/Program", "read,execute";
  permission java.lang.RuntimePermission "modifyThread";
  permission java.lang.RuntimePermission "modifyThreadGroup";
  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
  permission java.lang.RuntimePermission "loadLibrary.dt_shmem";
  permission java.util.PropertyPermission "java.home", "read";
  permission java.net.SocketPermission "<localhost>", "resolve";
  permission com.sun.jdi.JDIPermission "virtualMachineManager";
};

JPDA はリモートデバッグ機能をサポートしており、リモートホストからデバッガインタフェースにアクセスできる。適切に制限しなければ、攻撃者はこの機能を悪用して、実行中の Java プログラムが扱っているセンシティブな情報を取得したり、プログラムの動作を変更できてしまう。信頼できるホストのみにデバッガインタフェースの使用を許可するように、セキュリティマネージャで制限することができる。

Java SE の監視と管理機能

Java では、JVM の監視と管理のための様々な機能が提供されている[JMX 2006]。特に、Java Management Extension (JMX) API を使うことで、クラスのロード、スレッドの状態とスタックトレース、デッドロック検出、メモリ使用状況、ガベージコレクション、オペレーティングシステムに関する情報、その他様々な操作に関する監視と管理を行うことができる[Sun 04a]。また、監視と管理に関するログ機能も提供する。

Java SE の監視と管理機能は以下の4種類に分類される。

これらは(JVM が実行されている)ローカルマシン上で使うこともできるし、リモートから使うこともできる。JVM が実行を開始したとき、ローカルな監視と管理の機能はデフォルトで有効にされるが、リモートからの監視と管理の機能は無効になっている。リモートからの監視と管理の機能を有効にするには、(コマンドライン引数や設定ファイルで)数々のシステムプロパティを設定しなければならない。

リモートからの監視と管理の機能を有効にした場合、パスワード認証が行われるのがデフォルトの動作である。しかし、パスワード認証は無効にすることができる。パスワード認証を無効にすると、セキュリティレベルが低い状態になる。JMX サービスが待ち受けているポートに接続できれば、JVM で実行されている Java アプリケーションの監視と管理を誰でもできるようになるからである[JMXG 2006]。

JVM のリモートからの監視と管理機能は、安全な通信チャネル(Secure Sockets Layer [SSL])を使うのが、デフォルトの動作である。しかし、監視を行っているマシン上で、正規の remote method invocation (RMI) レジストリサーバが立ち上がる前に、攻撃者が独自の RMI レジストリサーバを立ち上げてしまうと、攻撃者に JMX パスワードを盗聴される可能性がある。また、リモートからの監視と管理機能で SSL を無効にすることができ、これもセキュリティ侵害につながる危険がある。さらなる詳細と対策については、「Java SE 監視および管理ガイド」 [JMXG 2006] を参照のこと。

リモートサーバの認証を要求する仕組みも提供されている。しかし、ユーザは、セキュリティを設定せずにリモートからの監視と管理機能を有効にして JVM を立ち上げてしまうことがありうる。そうすると、JVM は外部からの攻撃に晒されてしまう。リモートからの監視と管理機能を誤って有効にすることはないだろうが、有効にした状態で JVM を立ち上げた場合の危険性をユーザは理解していないかもしれない。

そのような JVM が攻撃を受けた場合、監視と管理機能は Java アプリケーションのセキュリティを著しく侵害する。たとえば攻撃者は、ロードされたクラスの数や実行中のスレッドの数、スレッドの状態、実行中のスレッドのトレース、システムプロパティ、 VM 引数、メモリ使用状況などを知ることができる。

違反コード (JVMTI)

以下の違反コード例では、実行中の JVM と通信するエージェントを使って VMTI が動作する。これらのエージェントは、通常、JVM の起動時にコマンドラインオプション -agentlib-agentpath で指定される。libname はロードするライブラリの名前、options はエージェントに起動時に渡される引数である。

${JDK_PATH}/bin/java -agentlib:libname=options ApplicationName

JVM の実装によっては、JVM が起動した後からエージェントを立ち上げることができる。これは、実運用環境ではセキュリティ上の問題となる。この機能の有効/無効に関するプラットフォーム個別の情報については、JVMTI のドキュメントを参照のこと[JVMTI 2006]。

環境変数をサポートしているプラットフォームでは、環境変数を使ってエージェントを指定することができる。「プラットフォームによっては、セキュリティ上の理由でこの機能を無効にしている。たとえば Unix システム上のリファレンス実装では、実効ユーザ ID が実ユーザ ID と異なったり、実効グループ ID が実グループ ID と異なっている場合に、この機能が無効になる」[JVMTI 2006]。

エージェントはデフォルトのセキュリティマネージャの下で特別なパーミッションを持たずに動作することができる。JVMTI はデバッガやプロファイラには役に立つ仕組みだが、実運用環境で使うには不適切である。

違反コード (JPDA)

以下の違反コード例では、コマンドライン引数で、共有メモリの使用と、そのトランスポートアドレス mysharedmemory を指定し、JVM を起動している。これによって JVM はデバッガの接続を待ち受けるようになり、デバッガアプリケーションを使ってデバッグ作業を行えるようになる。

${JDK_PATH}/bin/java -agentlib:jdwp=transport=dt_shmem,
    address=mysharedmemory ApplicationName

-agentlib 引数の代わりに、-Xrunjdwp および -Xdebug を使っても、同じようにデバッグ作業を行うことができる。(ただし、これらの引数は Java 1.5 以降では非推奨とされている。)

違反コード (JVM の監視)

以下の違反コード例では、ポート 8000 で接続して遠隔監視を可能にするようにコマンドライン引数を指定して、JVM を起動している。パスワードが簡単に推測できたり、通信に SSL プロトコルを使わないような場合、これは脆弱性となる。

${JDK_PATH}/bin/java
    -Dcom.sun.management.jmxremote.port=8000 ApplicationName
適合コード

以下の適合コードでは、エージェントを指定せずに JVM を起動している。-agentlib-Xrunjdwp-Xdebug といったコマンドライン引数は実運用環境では使わないこと。また、このコマンドライン例では、デフォルトのセキュリティマネージャもインストールしている。

${JDK_PATH}/bin/java -Djava.security.manager ApplicationName

空文字列を設定するなど、プラットフォームにおける適切な方法で、環境変数 JAVA_TOOL_OPTIONS の設定をクリアすること。そうすることで、監視と管理の仕組みを通じて JVMTI エージェントが引数を受け取ることを防ぐことができる。また、古い Java デバッガ oldjdb がサポートするデバッグ機能を無効にするため、コマンドライン引数 -Xnoagent を使うこともできる。

この適合コードでは、リモートからの監視機能を無効にしている。Java 6 では、ローカルの監視機能は有効になっているのがデフォルトの動作である。それより前のバージョンでは、ローカルの監視機能を有効にするためには、システムプロパティ com.sun.management.jmxremote を設定しなければならない。コマンドライン引数 -XX:+DisableAttachMechanism を使えば、Java ツールによるローカルの監視機能を無効にすることはできるが、ネイティブなデバッガやその他のツールを使えば、監視を行うことができる。幸い、監視ツールは少なくとも JVM プロセスの所有者と同じだけの権限を持っていなければならないので、ローカルな攻撃の危険は低減されている。

ローカルな監視では、ファイルパーミッションを JVM プロセスの所有者のものにした一時ファイルを使う。一時ファイルのアクセスが適切に制御されるよう、JVM が実行されるシステム上でファイルの保護が適切に行われていることを確認すること。詳細については、「FIO03-J. 一時ファイルはプログラムの終了前に削除する」を参照。

Java SE 監視および管理ガイド には、以下のようなアドバイスが書かれている [JMXG 2006]。

JConsole を使用してローカルアプリケーションを監視するのは、開発やプロトタイプ作成には便利だが、JConsole 自体がかなりのシステムリソースを消費するため、実運用環境で使用することは勧めない。JConsole アプリケーションを監視対象のプラットフォームから切り離すために、リモート監視を勧める。

jconsole をリモートシステムに置くことで、実運用環境におけるリソース消費を減らすことができる。

違反コード (遠隔からのデバッグ)

遠隔からのデバッグを行う場合、トランスポート層としてソケットを使う必要がある(transport=dt_socket)。また、アプリケーションの種類の指定(server=y、ここで y は JVM がサーバとなり、デバッガアプリケーションの接続を待ち受けることを表す)と、待ち受けるポート番号の指定(address=9000)も必要である。

${JDK_PATH}/bin/java -agentlib:jdwp=transport=dt_socket, 
    server=y,address=9000 ApplicationName

遠隔からのデバッグは危険である。攻撃者はクライアントの IP アドレスを詐称して JPDA ホストに接続するかもしれない。ネットワーク上で攻撃者がどこに位置するかによるが、JPDA ホストが詐称された IP アドレスに向けて送信するトラフィックを傍受して、デバッグ情報を入手する可能性がある。

適合コード (遠隔からのデバッグ)

セキュリティポリシーファイルを編集して、信頼するホストにのみ、遠隔からのデバッグに必要となる適切なパーミッションを割り当てること。たとえばパーミッション java.net.SocketPermission は JPDA ホストにのみ割り当て、その他のホストからは削除せよ。

JPDA ホストはサーバとしてもクライアントとしても動作させることができる。(たとえばセキュアチャネルの使用などにより)攻撃者がネットワーク通信を傍受して JPDA ホストを使う端末がどれなのかを調べることができないのであれば、server 引数に n を指定して、JPDA ホストをクライアント、デバッガアプリケーションをサーバとして設定するとよい。

この適合コードでは、信頼するデバッガアプリケーションに JPDA ホストが接続できるようにしている。

${JDK_PATH}/bin/java -agentlib:jdwp=transport=dt_socket, 
    server=n,address=9000 ApplicationName

デバッグ機能を有効にして JVM を実行する必要がある場合には、アプリケーションに不要なパーミッションを割り当てないこと。特に、任意のホストを対象とするソケット使用のパーミッションを割り当てないこと。つまり、パーミッション java.net.SocketPermission("*", "connect,accept") の割り当てを行わないこと。

例外

ENV05-EX0: ローカルな信頼境界の外からのアクセスが不可能であることを保証できるのであれば、ここで紹介したテクノロジーを使って Java プログラムのリモート監視を行ってもよい。たとえば、プログラムを実行するネットワーク環境全体が信頼できるものであり、インターネットのような信頼できないネットワークから切り離されているのであれば、リモート監視を行ってもよい。

リスク評価

JVMTI や JPDA やリモート監視機能を有効にして Java アプリケーションを実運用すると、攻撃者がアプリケーションの挙動を監視したり変更したりする危険がある。

ルール 深刻度 可能性 修正コスト 優先度 レベル
ENV05-J P18 L1
自動検出

このルールの違反は静的解析では検出できない。

関連する脆弱性

CVE-2010-4495 には TIBCO ActiveMatrix 製品群に存在した脆弱性について記されている。JMX 接続の処理に問題があり、遠隔のユーザによる任意コード実行、運用妨害(DoS)攻撃、センシティブな情報の取得が可能であった。

参考文献
[JMX 2006]  
[JMXG 2006]  
[JPDA 2004]  
[JVMTI 2006]  
[Long 2005] Section 2.6, The JVM Tool Interface; Section 2.7, Debugging; Section 2.8, Monitoring and Management
[Reflect 2006] Reflection, Sun Microsystems, Inc. (2006)
翻訳元

これは以下のページを翻訳したものです。

ENV05-J. Do not deploy an application that can be remotely monitored (revision 98)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter