System.exit() 呼出しはJava仮想マシン(JVM)を終了させる。つまり、実行中のすべてのプログラムやスレッドを終了させる。結果としてサービス運用妨害(DoS)を引き起こされる可能性がある。たとえば、Java Server Page(JSP)に組込まれたSystem.exit()を呼び出すことで、ウェブサーバを終了させ、プログラムがそれ以上サービスを提供できないようにすることが可能である。したがってプログラムは、突然の悪意ある System.exit() 呼出しを阻止しなくてはならない。さらに(Windows のタスクマネージャやPOSIXのkillコマンドの使用により)プログラムが強制的に終了させられる場合、必要なクリーンアップ処理を行わなくてはならない。
違反コード
以下の違反コード例では、System.exit()を使用してJVMを強制的にシャットダウンし、実行中のプロセスを強制終了している。このプログラムはセキュリティマネージャーを実装していないため、呼出し側がSystem.exit()を呼び出してよいかどうかをチェックすることができない。
public class InterceptExit { public static void main(String[] args) { // ... System.exit(1); // 突然の exit 呼出し System.out.println("This never executes"); } }
適合コード
以下の適合コードでは、SecurityManager クラスで定義されている checkExit() メソッドをオーバーライドする独自のセキュリティマネージャ PasswordSecurityManager を実装している。exitを許可する前にクリーンアップコードの呼出しを行うには、このようにオーバーライドする必要がある。SecurityManagerクラスに定義されているデフォルトのcheckExit()メソッドにはこの機能が存在しない。
class PasswordSecurityManager extends SecurityManager { private boolean isExitAllowedFlag; public PasswordSecurityManager(){ super(); isExitAllowedFlag = false; } public boolean isExitAllowed(){ return isExitAllowedFlag; } @Override public void checkExit(int status) { if (!isExitAllowed()) { throw new SecurityException(); } super.checkExit(status); } public void setExitAllowed(boolean f) { isExitAllowedFlag = f; } } public class InterceptExit { public static void main(String[] args) { PasswordSecurityManager secManager = new PasswordSecurityManager(); System.setSecurityManager(secManager); try { // ... System.exit(1); // 突然の exit 呼出し } catch (Throwable x) { if (x instanceof SecurityException) { System.out.println("Intercepted System.exit()"); // 例外をログに記録する } else { // 例外ハンドラに処理を渡す } } // ... secManager.setExitAllowed(true); // exit を許可する // System.exit() が実行される // ... } }
このプログラムでは、フラグを使ってexitが許可されているかどうかを確認している。setExitAllowed()メソッドはこのフラグを設定するが、フラグが設定されていない場合(つまりfalseの場合)、checkExit()メソッドはSecurityExceptionをスローする。このフラグは初期化時はfalseであるため、try 節では、最初のSystem.exit()を実行せずにSecurityException をスローする。プログラムはSecurityExceptionをキャッチし、例外をログに記録するなどのクリーンアップ処理を行う。クリーンアップ処理が完了して初めて System.exit() メソッドの実行が許可される。
例外
ERR09-EX0: コマンドラインプログラムにおいて、たとえば呼び出しに必要な引数が与えられていない場合などに System.exit() を呼び出すことは許される[Bloch 2008][ESA 2005]。
リスク評価
Sytem.exit()の不正な呼出しを許すと、サービス運用妨害(DoS)につながる恐れがある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
ERR09-J | 低 | 低 | 中 | P2 | L3 |
関連ガイドライン
MITRE CWE | CWE-382. J2EE bad practices: Use of System.exit() |
参考文献
[API 2006] | Method checkExit(), class Runtime, method addShutdownHook |
[Austin 2000] | Writing a Security Manager |
[Darwin 2004] | 9.5, The Finalize Method |
[ESA 2005] | Rule 78. Restrict the use of the System.exit method |
[Goetz 2006] | 7.4, JVM Shutdown |
[Kalinovsky 2004] | Chapter 16, Intercepting a Call to System.exit |
翻訳元
これは以下のページを翻訳したものです。
ERR09-J. Do not allow untrusted code to terminate the JVM (revision 70)