ugrás a tartalomhoz

JavaScript Sandbox tesztelése

zzrek · 2009. Május. 6. (Sze), 21.09
Sziasztok!

Nem teljesen megbízható forrásból származó javascript kódot kell futtatnom, ezért csináltam egy homokozó-szerűséget.

A lényege, hogy a beküldött kódot (ami egy függvény lehet) leellenőrzöm, kiveszem belőle a veszélyes elemeket (pl. eval), megnézem hogy milyen lokális változókat akar a kódbeküldő használni, és csupán egy golbális objektumhoz (a neve "arc"), ennek adataihoz és metódusaihoz engedem hozzáférni a kódot, a DOM-hoz, más külső forrásokhoz nem.

Azt hiszem jól kigondoltam az eljárást, de minden javaslatot szívesen fogadok (a szerver oldali ellenőrző algoritmussal, PHP nyelvvel kapcsolatban, de főleg hogy JS oldalon mit kellene még engedélyezni vagy milyen biztonsági rést hagytam meg, stb; illetve hogy egyes böngészők esetleg olyan egyedi JS metódusokat engednek, ami még veszélyes lehet stb stb.)

Ha valakinek van kedve, tesztelheti a homokozót, ezen a linken:
homokozó teszt

Ezen a linken megtalálhatjátok a szerver oldali forráskódot is.

A teszt lehet egy kis játék is:
A baloldalon szerkeszthető és "beküldhető" függvénnyel ha valakinek sikerült kijönnie a homokozóból, úgy mutathatná be, hogy hozzáfér a DOM-hoz, hogy az input2-ként megjelölt mezőbe beleír valamit.

Köszönöm a segítséget és a javaslatokat!
 
1

for in

kalmiz · 2009. Május. 6. (Sze), 22.59
utasítást is levédi, ami gondolom bug, mert nem szól érte, de aztán a runnál persze hibára fut a browser. Amúgy gratula nekem nem sikerült kitörnöm belőle. Böngésző specifikus dolgokat nem próbáltam.
3

Köszi, ez kimaradt.

zzrek · 2009. Május. 7. (Cs), 09.23
Köszi, ez kimaradt, javítom.
2

5 perc után

Poetro · 2009. Május. 7. (Cs), 00.56
var h2;
var d;
d = this["document"];
h2=d.createElement('h2');
d.getElementsByTagName('body')[0].appendChild(h2);
4

hopp!

zzrek · 2009. Május. 7. (Cs), 09.33
Köszi, szép volt!

Na lám ezt elfelejtettem kivédeni (márminthogy az asszoc. index tömbbel is elérhetők az objektumtulajdonságok).
Ez elgondolkodtató, hogy hogyan lehetne ezt megelőzni...

Azt hiszem hogy a legjobb, ha a "this" kulcsszót is tiltottá teszem.
Van még valamilyen globális objektum, amivel meg lehet ezt tenni?

Köszi ismét, pont az ilyenek miatt dobtam fel ezt a témát.

Mondjuk most beugrott, hogy miért hagytam meg direkt engedélyezettre a "this"-t: mert a későbbiekben ez is egy másik objektumra fog mutatni, így nem lesz veszélyes tulajdonsága. Igen, először ezt fogom tenni itt a tesztben is. (Persze ettől még kérdéses, hogy van-e egyéb objektum, ami kijáratot ad.)
5

Megjelent a v1.1

zzrek · 2009. Május. 7. (Cs), 09.48
Sziasztok!
Boldogan jelentem be, hogy megjelent az 1.1-es verzió, tesztelhető ugyanott:
sandbox 1.1

Az új változat -- köszönhetően a hozzászólók értékes segítségének -- 13%-kal biztonságosabb és 5%-kal kevesebb bugot tartalmaz.

A bátorító és segítő hozzászólásokat ismét szívesen fogadom és üzemszerűen gyűjtöm :-)
Köszönöm!
6

v1.11

zzrek · 2009. Május. 7. (Cs), 10.18
Javítottam egy kicsit rajta, mostmár nem kulcsszó után is elfogadja az objektumtulajdonságokat, szóval már ez is működik:

var s;
s={};s.a={};s.a.b=2;
alert(s["a"].b);
Azt hiszem ez nem ad biztonsági rést.
7

Hát ennek nincs sok teteje

zila · 2009. Május. 8. (P), 14.41
Ez annyira korlátozott homokozó, hogy értelmes dolgot alig lehet benne írni...

Ez például egy elfogadott js megoldás:
function teszt(par) {
     alma = function(par) {
             alert(par);
     };
     alma(par);
}
// Erre ezt kapom:
function teszt(par) {   Missing ')' or other syntax error. 
      alma = function(par) {   Invalid keyword: function 
            alert(arc.par); 
      }; 
      arc.alma(arc.par); 
}
A módosított függvény mindent csinál csak nem azt amit elvárnék tőle...

Vagy itt egy másik:
function teszt() 
{
d = {hello:  "szia ",
      belo :  "mia"};
alert(d.hello+d.belo);

}

// ez lesz belőle:
function teszt() 
{ 
arc.d = {arc.hello: "szia ", 
       arc.belo : "mia"}; 
alert(arc.d.hello+arc.d.belo); 

}
természetesen nem fut le a kód...
8

Csináljunk tetőt

zzrek · 2009. Május. 8. (P), 16.32
Szia!

Köszi az észrevételeket, látom lesz min dolgoznom még :-)

Szóval először is a legfontosabb: a leglényegesebb számomra hogy ne lehessen kijönni a homokozóból, az egyelőre másodlagos, hogy túlságosan lekorlátoz.
Ellenben viszont egyértelmű, hogy mindent meg akarok engedni ami nem veszélyes, az észrevételeid ebben mindenképp segítenek.

Az első példádban 2 hiányossága van a homokozónak:
1: (ezt könnyű javítani) nem engedi, hogy a függvénydefinícióban a kapcsos zárójel már az első sorban legyen (ezt megcsinálom)
2: a "function" kulcsszó nem engedélyezett pillanatnyilag.

Ez utóbbival több gondom van:
Előszöris egy rekurzív rendszerbe kell raknom a parsolást, hogy ha a függvényen belül deklarálva van egy újabb, akkor azon is végig kell menni (ezt még nem olyan vészes megoldani)
Másodszor figyelni kell a változó-hatáskörre: amit a külső függvényben lokálisan deklaráltam, az a belsőben elérhető (vagyis engedélyezett kulcsszó a belsőben is) ellenben a külsőben nem engedélyezett.

De azt hiszem, ha odafigyelek, abből nem lesz biztonsági gubanc, úgyhogy megcsinálom.

A 2. példáddal egyrészt semmi gondom nincsen, ha pongyola akarok lenni; másrészt viszont egyelőre nincs ötletem, hogy hogyan valósítsam meg.

A pongyola megoldás, hogy megkövetelem a felhasználóktól, hogy deklarálják le előre az objektumtulajdonságokat helyi változónak is:

 var d,hello,belo;

 d = {hello:  "szia ",  
       belo :  "mia"};  
 alert(d.hello+d.belo);
Ez így most működik.
(Vagy esetleg azt mondom hogy az object literal nem támogatott)

(
szerk:
egyébként így is jó: d={"hello":"szia"...
)

(Az alapelv, hogy az a kulcsszó, ami nincs helyi változóként var-ral elődeklarálva, az feltételezhetően és kötelezően az "arc" objektum része, és nem globális változó; azt ugyanis el akarom kerülni, hogy globális változókat használjanak, mert az összeütközéseket okozhat, és nem akarom azt sem, hogy globális változókhoz, objektumokhoz férjenek hozzá)

Viszont ha nem akarok pongyola lenni, akkor nincs túl jó ötletem arra, hogy hogyan ismerem fel, hogy egy kulcsszó (a példában a "hello" és a "belo") csupán egy, már előre helyileg deklarált objektum tulajdonsága.

Az szerinted úgy jó, ha egyszerűen megnézem, hogy "{" vagy "," és ":" között van-e?
A ternális operátor, amiben van még ":" az egyetlen ami bekavarhat... (vagy van más is?)

var a,b,c;
a=1;
b=((a==1)?1,e:2); //hibás
c=((a==1)?(1,e):2); // ez jó, viszont detektálható, mivel a "," és a ":" közt van egy ")"
Így elérhetik az "e" globális változót, ha esetleg valamelyik JS motor engedi zárójelezés nélkül is ezt a trükköt; sőt, lehet hogy még más trükk is van talán (???)

(Azon gondolkodok még, hogy segítene-e a "with" kifejezés használata a változók bekorlátozásában)

Minden javaslatot szívesen fogadok ennek a problémának a feloldására.
Köszönöm!
10

Sandbox V2.0 tetővel

zzrek · 2009. Május. 14. (Cs), 19.18
Sziasztok!

Nos, eljött a 2.0 ideje, object literal és belső függvény támogatással.
Örülnék, ha lenne valaki aki tesztelné, próbálgatná...
Sandbox 2.0

Köszönöm!
9

Más megközeltés

vbence · 2009. Május. 11. (H), 12.54
Én a JS felől közeltve egy ilyesmin gondolkodom:

	var SandboxedFunction = function (f) {
		this.containedFunction = f;
	}
		
	SandboxedFunction.prototype.invoke = function () {
		
		// a függvényünk futási kontextusa (a this-ként elért objektum)
		var containerObject = new Object ();
		
		// végigiterálunk a window objektumból "örökölt" változókon és helyi változókkal elfedjük őket
		var i;
		for (i in window) {
			eval("var " + i + " = new Object();");
		}		
		
		// a függvény string-reprezentációjában módostjuk a deklarációt és beágyazzuk
		var fs = new String(this.containedFunction);
		eval ("var tranformedFunction = function " + fs.substring(fs.indexOf('(')));
		
		// a beágyazott függvény futtatása az előkésztett kontextusban, a kapott paraméterekkel
		return tranformedFunction.apply(containerObject, arguments);
	}
A használata pedig körülbelül így:

	function gyanusOsszeadas(num1, num2) {		
		return num1 + num2;
	}
		
	var f = new SandboxedFunction(gyanusOsszeadas);
	var osszeg = f.invoke(3, 2);
	alert("az összeg: " + osszeg);
Persze aztán cizellálhatjuk a dolgot. Létrehozhatunk egy saját Window objektumot is olyan függvényekkel, amik 100%ig veszélytelenek. Vagy a biztonságos változókat tükrözhetjük a környezetbe (read-only jelleggel).

A Chrome picit megzavarodik, amikor window nevű helyi változót hoz létre az ember, úgyhogy ezen még dolgozni kell. A fenti kód Chome-ban volt tesztelve.
11

Szép!

zzrek · 2009. Május. 14. (Cs), 20.20
Tetszik a megközelítés, én is gondolkodtam ilyesmin. Hogyha minden ilyen nem megbízható függvényt külön iframe-be rejtenénk, akkor működne igazán jól a dolog, különben az esetleg használt (belül deklarált) globális változók összeakadhatnak.

(
Egyébként próbálgattam a kódodat, és érdekes módon az eval az működött, még nem jöttem rá miért:

    function gyanusOsszeadas(num1, num2) {
        glob=3;   
        return eval("num1 + num2+3");  
    }  
          
    var f = new SandboxedFunction(gyanusOsszeadas);  
    var osszeg = f.invoke(3, 2);  
    alert("az összeg: " + osszeg+ " glob:"+glob);

)

De más okok miatt akarom ezt mindenképp szerver oldalon levédeni, például azért, mert szerializálni akarok komplett objektumokat úgy, hogy a bennük lévő helyi változó pillanatnyi értéke is rögzüljön, ezt pedig nekem csak úgy sikerült megtennem, ha a függvényekben volt egy getter is ... meg egyéb olyan dolgokat akarok csinálni ami miatt a szerver oldali preparálás egyszerűbbnek (és talán hatékonyabbnak) tűnik (az én speciális esetemben).

Köszi az ötletet!
14

eval és globál

vbence · 2009. Május. 15. (P), 00.01
Az eval ebben a megoldásban nem jelent biztonsági kockátatot. Az evallal futtatott kód semmivel nem ér el több erőforrást, mint a függvény egyéb kódja. (Nálad az átalakítás miatt van tiltva).

A globális változó létrehozása valóban hiányossága a módszernek, azonban adat átvitelére nem alkalmas, hiszen a következő futáskor a függvény már maszkolva kapja meg. Legfeljebb, monoton növekvő számsorok átvitalére képes:

function gyanusOsszeadas(num1, num2) {  
    var i = 0;
	var lastType;
    do {
		eval("var lastType = typeof(ertekatvivoGlobal" + i + ");");
		if (lastType == "undefined")
			break;
        i++;
	} while (true);
	eval("ertekatvivoGlobal" + i + " = 1;");
	
	return i;
}
        
var f = new SandboxedFunction(gyanusOsszeadas);    
var osszeg;
osszeg = f.invoke(3, 2);    
alert("az összeg: " + osszeg); // 0

osszeg = f.invoke(3, 2);    
alert("az összeg: " + osszeg); // 1

osszeg = f.invoke(3, 2);    
alert("az összeg: " + osszeg); // 2

15

eval hogyan marad?

zzrek · 2009. Május. 15. (P), 11.32
Persze, az eval ugyanabban a kontextusban van, de nem ezen álltam meg, hanem az volt furcsa, hogy a for (i in window) az eval-t nem találta meg. (mondjuk ha abból is üres objektumot csinált volna, akkor a következő kulcsszónál már nem tudta volna végezni a dolgát)

A globális változókkal kapcsolatban most esett le, hogy az invoke minden gyanús függvényhívásnál végigjárja, immunizálja a window objektumot, ami nem túl hatékony. Talán az úgy jobb lenne, hogy csak egyszer állítanánk elő a futási környezetet, aztán már csak meghívnánk az előkészített függvényt. (A megvalósításon nem gondolkodtam, de gondolom closure-ral meg lehet csinálni). Ebben az esetben azonban megmarad a globális változós probléma.

Van azonban még egy gond: a homokozóba zárt eljárásoknak mindenképp kell egy külső objektum, amin keresztül dolgozhatnak (legalábbis az én alkalmazásomban ez mindenképp szükséges) tehát ezt az objektumot is minden meghíváskor klónozni kell.(Sőt talán hívás után még vissza is kell szinkronizálni a hivatalosan megváltoztatható adatokat?) (Míg szerver oldali feldolgozásnál egyszerűen nem engedek meg olyan parancsot kiadni, ami módosítaná az objektumot -- de ezzel még nem vagyok kész)
16

perzisztencia

vbence · 2009. Május. 15. (P), 14.30
Az eval nem a BOM része, hanem a nyelvé (amennyire tudom).

Ha szeretnél egy objektumot, ami megőrzi az értékét (gondolom az "arc" nálad erre szolgál), ezer és egy módon teheted elérhetővé a függvény számára. (Pl var, vagy a this-t pótoló objektumban).

Ki lehet egészíteni esetleg azzal a kódot, hogy a függvény lefutása után egy újabb for .. in végigmegy a window-n, és azokat a változókat, amik nem léteztek a függvényhívás előtt egyszerűen undefined-re állítja.
12

php javascript parser

Hodicska Gergely · 2009. Május. 14. (Cs), 21.27
http://j4p5.sourceforge.net/
J4P5 is a JavaScript interpreter written in PHP 5, that allows to run untrusted scripts in a sandbox on your server. It aims to implement most of Ecma-262 3d edition.
13

Ez azért teljesen más dolog...

zzrek · 2009. Május. 14. (Cs), 22.36
Ha jól értelmeztem, ez teljesen más dolog, hiszen a szerveren fut le a "JS" kód.
Ráadásul pillanatnyilag használhatatlan, és nem úgy tűnik, hogy ez változni fog (már 2005 óta nem fejlesztették???)

Mindenesetre az elképzelés jó, hasznos lehet.
17

Tipp

yaanno · 2009. Május. 20. (Sze), 15.00
Tudom hogy itt most a te megvalósításodról van szó, de esetleg nézd meg a Caja nevű eszközt ami valami hasonlót próbál csinálni.