Webエンジニア susumuis の技術ブログ

このブログの内容は個人の見解であり、所属する組織の公式見解ではありません

Javaによるお気楽なサンドボックス環境の作り方(セキュリティーマネージャ)

Javaで #appengine のようなサンドボックス環境を作る場合、SecurityManagerを使用するのがよさそうです。

http://itref.fc2web.com/java/security.html

具体的には、ポリシーファイルというものを書くという形になります。

(以下は転載)

grant {
permission java.util.PropertyPermission "test_prop", "write,read";
permission java.io.FilePermission "c:/temp/test.txt", "read,write";
//permission java.io.FilePermission "<>", "read,write";
};

しかし、今時のWebアプリなら、実行環境ごとに設定をお願いするのではなく、warをデプロイするだけで、必要な設定はなされていてくれる方がありがたいです。また、policyファイルのような外部ファイルを使うのではなく、純粋にJavaだけで制御すればデバッガもろもろが使えて便利ですよね?

policyファイルを使用しない方法は、java.lang.SecurityManager を独自にオーバーライドすることで実現できます。

http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/SecurityManager.html

そして、こんな感じにします。

public class MySecurityManager extends SecurityManager {
	@Override
	public void checkExit(int status) {
		throw new SecurityException("system exit is not supported.");
	}
}
System.setSecurityManager(new MySecurityManager());

しかし、これだと、環境にセキュリティーマネージャが設定されている場合は、その実装が殺されてしまいます。ということは、セキュリティーが考慮された環境でこれを行った場合、ヘタをすると、セキュリティーを低下させてしまいかねません。

※もちろん、よく設定された環境では、セキュリティマネージャの設定自体が許されない可能性もあります。

そう思うと気が重いことです。なかなか手をつけられません。

そこで、既存のセキュリティマネージャを生かしつつ、独自のセキュリティーチェックをJavaだけで実装しようとする場合は、以下のようにすれば良さそうです。

public class NullSecurityManager extends SecurityManager {
	@Override
	public void checkPermission(Permission perm, Object context) {
		// なにもしない
	}
	@Override
	public void checkPermission(Permission perm) {
		// なにもしない
	}
}
public class MySecurityManager extends NullSecurityManager {
	private SecurityManager innerSecurityManager;
	public MySecurityManager(SecurityManager innerSecurityManager) {
		if (innerSecurityManager == null) {
			innerSecurityManager = new NullSecurityManager();
		}
		this.innerSecurityManager = innerSecurityManager;
	}
	@Override
	public void checkPermission(Permission perm) {
		innerSecurityManager.checkPermission(perm);
	}
	@Override
	public void checkPermission(Permission perm, Object context) {
		innerSecurityManager.checkPermission(perm, context);
	}
	@Override
	public void checkCreateClassLoader() {
		innerSecurityManager.checkCreateClassLoader();
	}
	public void checkExit(int status) {
		innerSecurityManager.checkExit(status);
		throw new SecurityException("system exit is not supported.");
	}
	// 以下あらゆるメcheck系メソッドを委譲
}

あとは、Servletの起動時に

try {
	SecurityManager securityManager = System.getSecurityManager();
	if (!(securityManager instanceof MySecurityManager)) {
		System.setSecurityManager(new MySecurityManager(securityManager));
	}
} catch (SecurityException e) {
	System.out.println("cannot set my SecurityManager");
}

こんな感じで動きました。