2009/12/21
Rhinoのevalを使用禁止に
サーバ側のJSで eval() を使われちゃうとセキュリティ的にどうしようも無い。
口頭で禁止しても eval() の魔力に勝てず使ってしまう輩は必ずいる。
なので物理的に禁止する事にした。
ググって見たがそれっぽい記事は出て来ない。
しかたが無いので感でグローバル変数の eval を置き換えてみたらあっさり成功。
オリジナルの eval() を保存して置けばユーザ関数の実行中のみ禁止にもできる。
ユーザ関数の起動スクリプト:
ユーザ関数:
実行結果:
簡単すぎてなんか不安だけどちゃんと動いてるみたいなのでとりあえずこれでいっちゃおーっと。
口頭で禁止しても eval() の魔力に勝てず使ってしまう輩は必ずいる。
なので物理的に禁止する事にした。
ググって見たがそれっぽい記事は出て来ない。
しかたが無いので感でグローバル変数の eval を置き換えてみたらあっさり成功。
オリジナルの eval() を保存して置けばユーザ関数の実行中のみ禁止にもできる。
ユーザ関数の起動スクリプト:
function include(pageName) {
__ENV__.include(pageName, this);
}
function XMLHttpRequest() {
return new Packages.kotemaru.wsjs.ssjs.XMLHttpRequest(__ENV__);
}
(function(req, res) {
var evalBackup = eval;
eval = function() {
throw new Packages.kotemaru.wsjs.ErrorPageException(500,"evalDisabled");
}
if (req.method == "GET") {
doGet(req, res);
} else if (req.method == "POST") {
doPost(req, res);
} else {
throw "Not imple method "+req.method
}
eval = evalBackup;
})
ユーザ関数:
function doGet(req, res) {
eval("({a:'A'})");
}
実行結果:
2009/12/21 10:22:02.249 [ERROR ] ErrorPage:500 evalDisabled[] (ssjs-boot.js#16)
org.mozilla.javascript.JavaScriptException: ErrorPage:500 evalDisabled[] (ssjs-boot.js#16)
at org.mozilla.javascript.gen.c2._c4(ssjs-boot.js:16)
at org.mozilla.javascript.gen.c2.call(ssjs-boot.js)
at org.mozilla.javascript.ScriptRuntime.callSpecial(ScriptRuntime.java:2350)
at org.mozilla.javascript.optimizer.OptRuntime.callSpecial(OptRuntime.java:165)
at org.mozilla.javascript.gen.c10._c1(/home/kotemaru.openid.ne.jp/test3.ssjs:2)
at org.mozilla.javascript.gen.c10.call(/home/kotemaru.openid.ne.jp/test3.ssjs)
at org.mozilla.javascript.optimizer.OptRuntime.callName(OptRuntime.java:97)
at org.mozilla.javascript.gen.c2._c3(ssjs-boot.js:19)
at org.mozilla.javascript.gen.c2.call(ssjs-boot.js)
簡単すぎてなんか不安だけどちゃんと動いてるみたいなのでとりあえずこれでいっちゃおーっと。
2009/12/14
RhinoでJavaオブジェクトをfor-inする
RhinoからJavaオブジェクトを操っているとJavaオブジェクトのプロパティ一覧を for-in で取りたいことが良く有る。
で、自動のラッパーでなく ScriptableObject.defineClass() を使ってちゃんと定義すればできるんだろうと思ってやってみたらできねー。
ScriptableObjectの実装クラス。
実行スクリプト。
結果:空っぽ。
しかたが無いのでRhinoのソースを見て力技で実装したのがこのコード。
実行結果:fon-inできてます。
御覧の通りの力技なので私の試した rhino1_7R2 以外のバージョンで動くかは怪しいです。
正しいやり方をご存知の方はコメントお願いいたします。
で、自動のラッパーでなく ScriptableObject.defineClass() を使ってちゃんと定義すればできるんだろうと思ってやってみたらできねー。
ScriptableObjectの実装クラス。
package kotemaru.wsjs;
import org.mozilla.javascript.*;
public class UserConfig extends ScriptableObject {
private int timeout = 10;
private boolean server_log = false;
public String getClassName() { return "UserConfig"; }
public int jsGet_timeout() {
return timeout;
}
public void jsSet_timeout(int t) {
timeout = t;
}
public boolean jsGet_server_log() {
return server_log;
}
public void jsSet_server_log(boolean b) {
server_log = b;
}
public int getTimeout() {
return timeout;
}
public boolean isServerLog() {
return server_log;
}
}
実行スクリプト。
(function(dest, json) {
var src = eval("("+json+")");
dest = new UserConfig();
java.lang.System.out.println("-------->for-in UserConfig ");
for (var key in dest) {
java.lang.System.out.println("-------->"+key+":"+dest[key]+":"+src[key]);
dest[key] = src[key];
}
java.lang.System.out.println("-------->end");
})
結果:空っぽ。
-------->for-in UserConfig
-------->end
しかたが無いのでRhinoのソースを見て力技で実装したのがこのコード。
package kotemaru.wsjs;
import org.mozilla.javascript.*;
public class UserConfig /*extends ScriptableObject*/ {
private int timeout = 10;
private boolean server_log = false;
public int getTimeout() {
return timeout;
}
public boolean isServerLog() {
return server_log;
}
public void setTimeout(int t) {
timeout = t;
}
public void setServer_log(boolean b) {
server_log = b;
}
private static final String[] NAMES = {
"timeout",
"server_log",
};
public java.util.Iterator __iterator__(boolean b){
return new MyIterator(NAMES);
}
public JavaScriptException __getStopIteratin() {
Context cx = Context.getCurrentContext();
Scriptable scope = cx.initStandardObjects();
return new JavaScriptException(
NativeIterator.getStopIterationObject(scope), null, 0);
}
private class MyIterator implements java.util.Iterator {
private Object[] array;
private int idx = 0;
public MyIterator(Object[] ary) {array = ary;}
public boolean hasNext(){return idxpublic Object next(){
if (idx>=array.length) throw __getStopIteratin();
return array[idx++];
}
public void remove(){throw new Error("Unsupport");}
}
}
実行結果:fon-inできてます。
-------->for-in UserConfig
-------->timeout:10:30
-------->server_log:undefined:true
-------->end
御覧の通りの力技なので私の試した rhino1_7R2 以外のバージョンで動くかは怪しいです。
正しいやり方をご存知の方はコメントお願いいたします。
2009/12/12
RhinoのスレッドをTimeoutさせる。
WSJSのアプリを書いていて気が付いた。
スクリプトが無限ループすると止められない。
Thread.stop()は使用禁止のお達しが出ているしどうしたものかのうとググってみたら1件だけありました。
http://hydrocul.seesaa.net/article/118727709.html
日本語の情報はここだけみたい。
オリジナルの情報は Rhino の apidoc らしい。
http://www.mozilla.org/rhino/apidocs/org/mozilla/javascript/ContextFactory.html
おおざっぱに解説すると
- Context と ContextFactory を自前で拡張実装する。
- インターバルを設定すると ContextFactory のコールバッグが呼ばれる。
- Context にタイムスタンプを持たしてコールバック内で適当に例外を上げる。
と言うことになる。
タイムアウトなんて普通に必要になりそうなんだから標準実装でもよさそうな気がするけどなぁ。
ま、それはそれとしてWSJSに組み込み。
メインクラスにこれが必要。
SsjsContextFactory.java:
SsjsContext.java:
テストコード、いつもより多く回っております。
30秒後にめでたく例外発生。
サンプルと違いタイムアウトのチェックを Context に持って来ているのはアプリからタイムアウトを制御できるようにするため。
これができないとリモートからスクリプト記述する場合にかなり問題になるので助かった。
スクリプトが無限ループすると止められない。
Thread.stop()は使用禁止のお達しが出ているしどうしたものかのうとググってみたら1件だけありました。
http://hydrocul.seesaa.net/article/118727709.html
日本語の情報はここだけみたい。
オリジナルの情報は Rhino の apidoc らしい。
http://www.mozilla.org/rhino/apidocs/org/mozilla/javascript/ContextFactory.html
おおざっぱに解説すると
- Context と ContextFactory を自前で拡張実装する。
- インターバルを設定すると ContextFactory のコールバッグが呼ばれる。
- Context にタイムスタンプを持たしてコールバック内で適当に例外を上げる。
と言うことになる。
タイムアウトなんて普通に必要になりそうなんだから標準実装でもよさそうな気がするけどなぁ。
ま、それはそれとしてWSJSに組み込み。
メインクラスにこれが必要。
static {
ContextFactory.initGlobal(new SsjsContextFactory());
}
SsjsContextFactory.java:
package kotemaru.wsjs.ssjs;
import org.mozilla.javascript.*;
public class SsjsContextFactory extends ContextFactory {
private static final int LIMIT_TIME = 30*1000; //30sec
protected Context makeContext(){
SsjsContext cx = new SsjsContext();
cx.setInstructionObserverThreshold(LIMIT_TIME/10);
return cx;
}
protected Object doTopCall(Callable callable, Context cx,
Scriptable scope, Scriptable thisObj, Object[] args){
long curTime = System.currentTimeMillis();
((SsjsContext)cx).setTimeout(curTime + LIMIT_TIME);
return super.doTopCall(callable, cx, scope, thisObj, args);
}
protected void observeInstructionCount(Context cx, int instructionCount){
((SsjsContext)cx).checkTimeout();
}
}
SsjsContext.java:
package kotemaru.wsjs.ssjs;
import org.mozilla.javascript.*;
import kotemaru.wsjs.*;
public class SsjsContext extends Context {
private long timeout;
public SsjsContext() {
}
public void setTimeout(long t) {
timeout = t;
}
public void checkTimeout() {
if(timeout < System.currentTimeMillis()) {
throw new ErrorPageException(503,"JavaScriptTimeout");
}
}
}
テストコード、いつもより多く回っております。
function doGet(req, res) {
var timeout = __ENV__.currentTimeMillis() + 60*1000;
while (timeout > __ENV__.currentTimeMillis()) {
"aaa"+"bbbb";
}
}
30秒後にめでたく例外発生。
ErrorPage:503 JavaScriptTimeout[]
at kotemaru.wsjs.ssjs.SsjsContext.checkTimeout(SsjsContext.java:16)
at kotemaru.wsjs.ssjs.SsjsContextFactory.observeInstructionCount(SsjsContextFactory.java:22)
at org.mozilla.javascript.Context.observeInstructionCount(Context.java:2251)
at org.mozilla.javascript.ScriptRuntime.addInstructionCount(ScriptRuntime.java:3110)
サンプルと違いタイムアウトのチェックを Context に持って来ているのはアプリからタイムアウトを制御できるようにするため。
これができないとリモートからスクリプト記述する場合にかなり問題になるので助かった。