環境変数およびシステムプロパティはどちらも、利用者がキーとそれに対応する値を定義し、それらの値を実行環境からプロセスへと伝えるために用いられる。Java API [API 2006]の java.lang.System クラスには以下のように記述されている。
環境変数は、よりグローバルな効果を及ぼす。これは、直接の Java サブプロセスのみでなく、プロセスにより定義されたすべての子孫に環境変数がみえるからである。これらは、異なるオペレーティングシステム上で微妙に異なるセマンティクス(大文字と小文字を区別しないなど)を持つ可能性がある。これらの理由から、環境変数は意図しない副作用を持つ可能性が高くなる。可能な場合は、システムプロパティを使用することがもっとも適切である。環境変数は、グローバルな効果が必要なときや、外部システムインタフェースが環境変数(PATH など)を必要とするときに使うべきである。
自身が置かれた環境よりも信頼度が高いドメインで実行されるプログラムは、環境変数の値は信頼できないものと想定し、その値を使用する前に無害化と検証を行わなくてはならない。
システムプロパティのデフォルト値は、Java仮想マシン(JVM)により起動時に設定されるものであり、この値は信頼できるものであろう。しかし、これらデフォルト値は、設定ファイル等の信頼できない入力から取得されたプロパティによって上書きされているかもしれない。したがって、信頼できない入力から取得したシステムプロパティは、使用する前に無害化と検証を行わなくてはならない。
Java チュートリアルには以下のように記されている[Campione 1996]。
可搬性を高めるために、環境変数の値と同じ値をシステムプロパティを使って取得できるのであれば、環境変数の値を参照すべきではない。たとえば、オペレーティングシステムがユーザ名を扱っている場合、システムプロパティ user.name から取得できる。
実際のところ、環境変数への依存は可搬性の問題だけでは済まない。攻撃者は java.lang.ProcessBuilder クラスなどのメカニズムを使用することで、プログラムで使われるすべての環境変数の値を制御できる。
したがって、環境変数の値に含まれる情報を、システムプロパティを含む他の手段で取得できるならば、環境変数は使用してはならない。また、使用するのであれば、環境変数は適切な検証を行った上で用いること。
違反コード
以下の違反コード例は、環境変数を使ってユーザ名を取得しようとしている。
String username = System.getenv("USER");
このコードにはまず可搬性の問題がある。Java チュートリアルは以下のように推奨している。
環境変数の用いられ方は実行環境によって異なる。たとえば、Windows におけるユーザ名は、環境変数 USERNAME で表されるが、UNIX におけるユーザ名は USER、LOGNAME、あるいはその両方で表される。
さらに攻撃者は、環境変数 USER の値を任意の値に書き換えた上でこのプログラムを実行することができる。以下のコード例はまさにこの攻撃を POSIX プラットフォームにおいて行うものである。
public static void main(String args[]) { if (args.length != 1) { System.err.println("Please supply a user name as the argument"); return; } String user = args[0]; ProcessBuilder pb = new ProcessBuilder(); pb.command("/usr/bin/printenv"); Map<String,String> environment = pb.environment(); environment.put("USER", user); pb.redirectErrorStream(true); try { Process process = pb.start(); InputStream in = process.getInputStream(); int c; while ((c = in.read()) != -1) { System.out.print((char) c); } int exitVal = process.waitFor(); } catch (IOException x) { // ハンドラに処理を移す } catch (InterruptedException x) { // ハンドラに処理を移す } }
このプログラムは、環境変数とその値を出力する POSIX の /usr/bin/printenv コマンドを実行する。プログラムは、単一の文字列引数をとり、USER 環境変数の値をその文字列に設定する。printenv の出力を見ると、環境変数 USER が与えられた引数に設定されていることがわかる。
適合コード
以下の適合コードでは user.name システムプロパティを使ってユーザ名を取得している。USER 環境変数の値が間違った値に設定されていたり、あるいはそもそも存在しない場合であっても、JVMはその初期化時に、このシステムプロパティを正しいユーザ名に設定する。
String username = System.getProperty("user.name");
リスク評価
信頼できない環境変数を適切に無害化せずに使うと、データインジェクションやその他の攻撃を受ける危険がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
ENV02-J | 低 | 高 | 低 | P9 | L2 |
参考文献
[API 2006] | |
[Campione 1996] |
翻訳元
これは以下のページを翻訳したものです。
ENV02-J. Do not trust the values of environment variables (revision 22)