Schlechtes Coding kann zu einer XSS in Ruby on Rails führen

Letztes Jahr (circa Mitte Oktober) arbeitete Sebastian an einem Projekt in Ruby on Rails. Während der Implementierung einer Funktion fiehl ihm auf, dass es durch das Schachteln von “Form helpers” möglich war eine XSS auszuführen.

Ruby on Rails verfügt grundsätzlich über einen hohes Sicherheitskonzept. Der ORM (Object relational Mapper) macht SQL Injections nahezu unmöglich und das Template-System trennt Daten vom Template, sodass XSS bestmöglich verhindert wird. Hinzu kommt, dass Rails eine Menge nützlicher Funktionen zur Erstellung von HTML bietet. Zum Beispiel kann man mit dem folgenden Code ein Icon erstellen, welches mit einer bestimmten Aktion verknüpft wird:

1
link_to (content_tag(:i,'', :class => 'action-icon icon-trash')), controller_path(@controller), method: :patch, name: "dofoo"

Während des Programmierens fiehl auf, dass durch das Schachteln der “button_to” und der “content_tag” Funktion (z.B. dem Erstellen eines Icons in einem Button - witzige Idee ;)) etwas unerwartetes geschah:

1
button_to (content_tag(:i,'', :class => 'action-icon icon-trash')), { param: id }, method: :put

Daraus ergab sich folgender HTML Code:

1
<input type="submit" value="<i class=" action-icon="" icon-trash"="">" />

Der Grund für das Fehlverhalten ist folgender (Herr Koziarski vom Rails security team hat entsprechend auf unsere Anfrage geantwortet):

Warum an dieser Stelle ein fehlerhaftes HTML ausgegeben wird, hat mit dem Umstand zu tun, dass “content_tag” ein String zurückgibt, welcher als html_safe? makiert ist und solche Strings werden direkt ausgegeben und nicht nochmals maskiert.
Auf den ersten Blick funktioniert der Code in “button_to” korrekt, aber das eigentliche Ergebnis ist natürlich nicht wünschenswert.

Die gesamte XSS-Protection wurde unter der Annanme erstellt, dass ein Angreifer niemals html_safe? Strings generieren kann und die einzige sonstige Funktion, die solche Dinge als sicher deklariert ist der Rails-Helper (welcher wie intendiert sämtliche Strings prüft) oder Nutzeingaben welche zuvor allerdings durch eine ensprechende Prüffunktion (etwa eine santize oder html_escape Funktion) gelaufen sind.

Herr Koziarski übersetzt

Ein weiterer Blick auf diese Konstellation führte zu einem brauchbaren Angriffsszenario. Beispielsweise indem man die Nutzereingabe als class-Attribute übergibt:

1
2
%testdiv
  =button_to (content_tag(:i,'', :class => params[:test])), {param: 1 }, method: :put

Wenn nun ein Nutzer mit dem Testparameter “?test=onmouseover=alert(1)//” eine entsprechende Website aufruft, führt das zu folgender HTML Ausgabe:

1
<input type="submit" value="<i class=" onmouseover="alert(1)//"">

Diese Art des Angriffs ist allerdings sehr unwahrscheinlich, denn dazu müsste der Entwickler den o.g. (schrecklichen) Code schreiben. Das ist der Grund, weshalb das Security Team von Rails sich nicht im Zugzwang sah einen Fix für dieses Fehlverhalten zu bieten.

Es ist sehr unwahrscheinlich, das solcher Code im Produktionsumfeld laufen würde, insoweit hat das keine besonders hohe Priorität für uns. Vorallem gäbe es Schwierigkeiten beim Maskieren der Daten - html_escape ist idempotent und maskiert bereits maskierte Strings nicht.

Herr Koziarski übersetzt

Unsere gesamten Tests haben wir unter folgenden Systemen/Systembedingungen ausgeführt:

1
2
3
4
- Ruby 2.0.0
- Rails 4.0
- Chromium-Browser 
- Puma als rails-server

Wir danken dem Rails Team für die freundliche Unterstützung. Auch wenn die Lücke vorerst offen bleibt, war es eine spannende Erfahrung einen solchen Bug einmal genauer zu betrachten.

Das Team der Internetwache.org