Der Online-Interpreter (www.ruby.ch) macht sich den eval()-Befehl zu Nutze, der eine beliebige (Ruby-)Codesequenz ausführen kann. Da dies Gefahren in sich birgt - etwa (system(rm -r /)) -, muss eine Sandbox (à la Java-Applets) her. [#listing3 Listing 3] zeigt die Implementierung der Sandbox.
Als Erstes sei auf die Sicherheitsmechanismen von Ruby hingewiesen. Die globale Variable $SAFE verwaltet die Sicherheitsstufen. Möglich sind 0 bis 5. Je nach Stufe verhindert der Interpreter die eine oder andere Operation. Für diese Sandbox wurde Stufe 2 gewählt.
Zudem geschieht die Ausführung des Codes in einem niedrigpriorisierten Thread. Den überwacht ein Watchdog-Thread, der sich auf folgendes Missverhalten konzentriert:
eval() erwartet als zweiten Parameter einen Kontext, in dem es den Code ausführen soll. Das ist in diesem Falle der Hauptkontext. In ihr werden kritische System-Funktionen überladen und somit in diesem Kontext verhindert.
:syntaxErrorDetected, :syntaxErrorText, :sandboxOutput
- class Sandbox
- attr_reader :securityViolationDetected, :securityViolationText,
- def initialize(level = 2, maxRunTime = 10, maxThreadCount = 10, maxNewObjects = 10000)
- @level = level
- @maxRunTime = maxRunTime
- @maxThreadCount = maxThreadCount
- @maxNewObjects = maxNewObjects
- @securityViolationDetected = false
- @securityViolationText = ''
- 0 @syntaxErrorDetected = false;
- 1 @syntaxErrorText = ''
- 2 @sandboxOutput = ''
- 3 @sandboxThreadGroup = ThreadGroup.new
- 4 Thread.abort_on_exception= true
- 5 @done = false
- 6 end
- 7 def threadCount
- 8 return Thread.list.size()
- 9 end
- 0 def raiseSecurityError(text)
- 1 raise (SecurityError, text)
- 2 end
30 if (runningForSecs
- 3 def startWatchDogThread
- 4 @watchdogThread = Thread.new {
- 5 ObjectSpace.garbage_collect # force a collection here.
- 6 objectsBeforeLaunch = ObjectSpace.each_object {}
- 7 runningForSecs = 0
- 8 begin
- 9 while not(@done) do
>@maxRunTime) then34 if ((ObjectSpace.each_object {} - objectsBeforeLaunch)
- 1 raiseSecurityError("Your script may only run for #{@maxRunTime}sec")
- 2 end
- 3 ObjectSpace.garbage_collect # force a collection here.
>@maxNewObjects) then37 if (threadCount()
- 5 raiseSecurityError("You may only create #{@maxNewObjects} objects")
- 6 end
>@maxThreadCount) then 38 raiseSecurityError("You may only use #{@maxThreadCount} Threads") 39 end 40 sleep 1 41 runningForSecs += 1 42 end # while 43 rescue SecurityError =>detail 44 @securityViolationDetected = true 45 @securityViolationText = detail 46 sleep .1 47 @sandboxThreadGroup.list.each { | th | 48 th.kill 49 } 50 @done = true 51 end 52 } # end_of_thread 53 end
- 4 def executeItInTheSandbox(exeCmd)
- 5 begin
- 6 eval(exeCmd, Object.module_eval("binding"))
- 7 rescue SecurityError =
>detail- 8 @securityViolationDetected = true
- 9 @securityViolationText = detail
- 0 rescue Exception =
>detail- 1 @syntaxErrorDetected = true
- 2 @syntaxErrorText = detail
- 3 end
- 4 end
- 5 def execute(cmd)
- 6 cmd.untaint
- 7 exeCmd = cmd
- 8 exeCmd = "$SAFE = #{@level}\n" + exeCmd
- 9 startWatchDogThread() # this is our BIG BROTHER
- 0 @sandboxThreadGroup.add(@sandboxThread = Thread.new {
- 1 executeItInTheSandbox(exeCmd)
- 2 })
- 3 @sandboxThread.priority= -5 # very low priority
- 4 @sandboxThread.join() # wait for completion
- 5 @done = true
- 6 @watchdogThread.join()
- 7 end
- 8 end # class Sandbox
Dieser Text ist der Zeitschriften-Ausgabe 05/2001 von iX entnommen.
iOS, Android, Windows Phone 7 und HTML5 - das neue Sonderheft von heise Developer führt Einsteiger und Profis in die Programmierung mobiler Geräte ein.