Ruby Sicherheit
Die Programmiersprache Ruby ist groß und komplex, und es gibt viele Sicherheitsfallen, auf die oft sowohl Neulinge als auch erfahrene Rubyisten stoßen.
Dieses Dokument zielt darauf ab, viele dieser Fallstricke zu diskutieren und, wo anwendbar, sicherere Alternativen anzubieten.
Bitte überprüfen Sie die vollständige Liste der öffentlich bekannten CVEs und wie Sie eine Sicherheitslücke korrekt melden können, unter: www.ruby-lang.org/en/security/ Die japanische Version finden Sie hier: www.ruby-lang.org/en/security/
Sicherheitslücken sollten per E-Mail an security@ruby-lang.org (den öffentlichen PGP-Schlüssel) gemeldet werden, was eine private Mailingliste ist. Gemeldete Probleme werden nach der Behebung veröffentlicht.
Marshal.load
Das Ruby-Modul Marshal bietet Methoden zum Serialisieren und Deserialisieren von Ruby-Objektstrukturen in und aus einem binären Datenformat.
Verwenden Sie niemals Marshal.load, um nicht vertrauenswürdige oder vom Benutzer bereitgestellte Daten zu deserialisieren. Da Marshal fast jedes Ruby-Objekt deserialisieren kann und die volle Kontrolle über Instanzvariablen hat, ist es möglich, eine bösartige Nutzlast zu erstellen, die kurz nach der Deserialisierung Code ausführt.
Wenn Sie nicht vertrauenswürdige Daten deserialisieren müssen, sollten Sie JSON verwenden, da dies nur in der Lage ist, „primitive“ Typen wie Zeichenketten, Arrays, Hashes, Zahlen und nil zurückzugeben. Wenn Sie andere Klassen deserialisieren müssen, sollten Sie dies manuell handhaben. Deserialisieren Sie niemals in eine vom Benutzer angegebene Klasse.
YAML
YAML ist ein beliebtes, menschenlesbares Datenformat für die Serialisierung, das von vielen Ruby-Programmen für die Konfiguration und die Persistenz von Ruby-Objektstrukturen in Datenbanken verwendet wird.
Ähnlich wie Marshal kann es in beliebige Ruby-Klassen deserialisieren. Zum Beispiel werden die folgenden YAML-Daten bei der Deserialisierung ein ERB-Objekt erstellen, wenn die Methode unsafe_load verwendet wird.
!ruby/object:ERB src: puts `uname`
Aus diesem Grund sind viele der Sicherheitsüberlegungen, die für Marshal gelten, auch auf YAML anwendbar. Verwenden Sie YAML nicht zur Deserialisierung nicht vertrauenswürdiger Daten.
Symbole
Symbole werden oft als Syntaxzucker für einfache Zeichenketten angesehen, aber sie spielen eine wesentlich wichtigere Rolle. Die MRI-Ruby-Implementierung verwendet Symbole intern für Methodennamen, Variablennamen und konstante Namen. Der Grund dafür ist, dass Symbole einfach ganze Zahlen mit angehängten Namen sind, wodurch sie in Hashtabellen schneller nachgeschlagen werden können.
Die meisten Symbole können durch die Garbage Collection entfernt werden; diese werden als mortal (sterbliche) Symbole bezeichnet. Die meisten Symbole, die Sie erstellen (z. B. durch Aufruf von to_sym), sind mortal.
Immortal (unsterbliche) Symbole hingegen werden niemals durch die Garbage Collection entfernt. Sie werden erstellt, wenn Code modifiziert wird
-
eine Methode definiert wird (z. B. mit
define_method), -
eine Instanzvariable gesetzt wird (z. B. mit
instance_variable_set), -
eine Variable oder Konstante erstellt wird (z. B. mit
const_set)
C-Erweiterungen, die nicht aktualisiert wurden und immer noch SYM2ID aufrufen, erstellen immortale Symbole.
Erstellen Sie keine immortalen Symbole aus Benutzereingaben. Andernfalls könnten Benutzer einen Denial-of-Service-Angriff auf Ihre Anwendung starten, indem sie diese mit eindeutigen Zeichenketten fluten, was dazu führt, dass der Speicher unendlich wächst, bis der Ruby-Prozess beendet wird oder das System verlangsamt.
Obwohl es keine gute Idee sein mag, diese mit Benutzereingaben aufzurufen, sind Methoden, die früher anfällig waren, wie to_sym, respond_to?, method, instance_variable_get, const_get usw. keine Bedrohung mehr.
Reguläre Ausdrücke
Die Syntax von Ruby für reguläre Ausdrücke unterscheidet sich geringfügig von der in anderen Sprachen. In Ruby beziehen sich die Anker ^ und $ nicht auf den Anfang und das Ende der Zeichenkette, sondern auf den Anfang und das Ende einer **Zeile**.
Das bedeutet, dass, wenn Sie einen regulären Ausdruck wie /^[a-z]+$/ verwenden, um eine Zeichenkette auf Buchstaben zu beschränken, ein Angreifer diese Überprüfung umgehen kann, indem er eine Zeichenkette mit einem Buchstaben, dann einem Zeilenumbruch und dann einer beliebigen Zeichenkette seiner Wahl übergibt.
Wenn Sie den Anfang und das Ende der gesamten Zeichenkette in Ruby abgleichen möchten, verwenden Sie die Anker \A und \z.
eval
Übergeben Sie niemals nicht vertrauenswürdige oder vom Benutzer kontrollierte Eingaben an eval.
Wenn Sie keine REPL wie irb oder pry implementieren, ist eval fast sicher nicht das, was Sie wollen. Versuchen Sie nicht, Benutzereingaben zu filtern, bevor Sie sie an eval übergeben – dieser Ansatz ist voller Gefahren und wird Ihre Anwendung höchstwahrscheinlich einer ernsthaften Remote-Code-Ausführungs-Schwachstelle aussetzen.
send
„Globale Funktionen“ in Ruby (puts, exit usw.) sind tatsächlich private Instanzmethoden von Object. Das bedeutet, dass es möglich ist, diese Methoden mit send aufzurufen, selbst wenn der Aufruf von send einen expliziten Empfänger hat.
Zum Beispiel schreibt der folgende Code-Schnipsel „Hello world“ in das Terminal
1.send(:puts, "Hello world")
Sie sollten send niemals mit benutzereingaben als erstem Parameter aufrufen. Dies kann eine Denial-of-Service-Schwachstelle einführen.
foo.send(params[:bar]) # params[:bar] is "exit!"
Wenn ein Angreifer die ersten beiden Argumente von send kontrollieren kann, ist eine Remote-Code-Ausführung möglich.
# params is { :a => "eval", :b => "...ruby code to be executed..." } foo.send(params[:a], params[:b])
Wenn Sie einen Methodenaufruf basierend auf Benutzereingaben veranlassen, verifizieren Sie sorgfältig den Methodennamen. Wenn möglich, überprüfen Sie ihn anhand einer Whitelist sicherer Methodennamen.
Beachten Sie, dass die Verwendung von public_send ebenfalls gefährlich ist, da send selbst öffentlich ist.
1.public_send("send", "eval", "...ruby code to be executed...")