Ruby hat seit jeher ein mehrstufiges Sicherheitskonzept implementiert. Damit kann gesteuert werden, welche sicherheitsrelevanten Aktionen mit einem Ruby Script ausgeführt werden dürfen.
Sicherheitsrelevante Operationen
Gefährliche Operationen aus Ruby Sicht sind Zugriffe auf Umgebungsvariablen, das Dateisystem oder Kommandozeilenbefehle. Also Zugriffe auf Entitäten, die außerhalb von Ruby selbst definiert sind.
Alles, was aus diesen Quellen stammt ist unsicher (oder Tainted):
File.read('README.md').tainted? # => true
ENV['LANG'].tainted? # => true
Intern erzeugte Objekte dagegen nicht:
'Ruby'.tainted? # => false
Fixnum.tainted? # => false
Das Tainted Flag ist an jedem Objekt vorhanden und je nach SAFE Level können mit Tainted Objekten Operationen ausgeführt werden oder nicht.
Die Tainted Eigenschaft wird Objekte weitergegeben:
home = ENV['HOME']
home.tainted? # => true
user = home.split('/')[-1]
user.tainted? # => true
Ruby SAFE Levels
Ruby Skripte können seit der Version 2.3.0 in zwei verschiedenen SAFE Level (0 und 1) ausgeführt werden. Die Level 2 - 4 werden seitdem nicht mehr unterstützt:
$SAFE = 3 # => $SAFE=2 to 4 are obsolete (ArgumentError)
Standardmäßig ist das Level 0 gesetzt:
$SAFE # => 0
SAFE Level können nur hochgesetzt, aber nie runtergesetzt werden:
$SAFE # => 0
$SAFE = 1
$SAFE # => 1
$SAFE = 0 # => SecurityError: tried to downgrade safe level from 1 to 0
Ruby - Tainted Love
Was passiert, wenn mit einem Tainted Objekt eine Operation ausgeführt wird, die in dem SAFE Level nicht erlaubt ist, soll kurz demonstriert werden.
# operation.txt
puts 'Dangerous operation.'
Die Textdatei wird eingelesen:
operation = File.read('operation.txt')
Im SAFE Level 0 kann dieser String evaluiert werden, im SAFE Level 1 nicht mehr:
$SAFE # => 0
eval(operation) # => "Dangerous operation."
$SAFE = 1
eval(operation) # => SecurityError: Insecure operation - eval
Ein Objekt kann auch als Tainted markiert werden:
ruby = 'Ruby'
ruby.tainted? # => false
ruby.taint
ruby.tainted? # => true
und auch wieder Untainted werden:
ruby.untaint
ruby.tainted? # => false
Allerdings sollte dann auch klar sein, ob der externen Quelle vertraut werden kann oder nicht.
Klassen können konsequenterweise ebenfalls Tainted werden, was aber nicht dazu führt, dass Instanzen der Klasse ebenfalls Tainted sind:
String.tainted? # => false
String.taint
String.tainted? # => true
String.new.tainted? # => false
Andere Ruby Implementierungen, wie JRuby haben keine SAFE Level implementiert. JRuby basiert auf dem Sicherheitskonzept der JVM.