Meine ehemalige Uni hat ein nettes System im Probebetrieb gestartet:
Webtasks
Idee hinter dem System ist, dass angehende Stundenten kleine Programmieraufgaben und Wissenstests online lösen können. Zwar noch stellenweise recht buggy, aber vom Ansatz her wirklich geil.
Interessant für fortgeschrittene Programmierer ist hierbei die eingehende Analyse des System bzw der Absicherung

Viel Spaß beim Lesen der folgenden Seiten
Bei den ersten Aufgaben bekommt man auf der Weboberfläche immer die Möglichkeit den Methodenrumpf einzugeben - Anfang und Ende der Methode ist vorgegeben.
Nachdem man also seinen Sourcecode eingeben hat, fällt schnell einiges auf:
Die Entwickler von Webtasks haben das Schlüsselwort
System gesperrt. Somit fallen viele schädliche Funktionen wie beispielsweise
exit(int) weg. Aber leider auch echt nützliche Funktionen wie
arraycopy
Aber als erfahrener Sicherheitsspezialist sollte man wissen, dass ein
Sperren von Usereingaben aufgrund einer Blacklist immer aufwändig und häufig fehlerhaft ist.
So bietet sich dank Reflection ein Weg trotzdem auf alle System-Funktionen zuzugreifen:
CODE:
try
{
Class c = Class.forName("java.lang.System");
}
catch (Exception e)
{
// Errorhandling
}
Aber es wurden so auch nette Klassen wie beispielsweise
Runtime übersehen, vergessen oder ignoriert
Eine komplette Übernahme des Servers wird aber vom strikt eingestellten
SecurityManager verhindert - jeglicher Zugriff auf Kommandos, Dateizugriffe allgemein sowie Sockets ist gesperrt.
Aber es gibt ja noch mehr Optionen zu "stören". Beispielsweise alle Rechenzeit durch eine Endlosschleife bzw Rekursion ohne Abbruchbedindung aufzubrauchen. Ein Verbrauch der Speicherressourcen ist erstmal nicht möglich da die Java-VM beim Starten feste Speichergrenzen übergeben bekommen hat.
Bei den ersten Versuchen zeigt sich, dass die eingesetzten Java-Unittests Zeitlimits gesetzt haben - eine Endlosschleife wird nach 30 Sekunden abgebrochen. Auch eine simple Rekursion wird entdeckt und verhindert: "Rekursiver Aufruf verboten!"
Da erstmal nicht klar ist, wie die Rekursion entdeckt wird (der gesamte Sourcecode liegt ja nicht vor - mehr dazu aber später), kann man einfach eine s.g. unchecked Exception werfen und sich über die Ausgabe des Compilers freuen:
CODE:
|*Append.java:53: unreachable statement*
semaphore = false;
^
1 error
Ah, eine
Semaphore 
Und wir wissen nun nicht nur den Dateinamen sondern auch noch den Variablenname der Semaphore sowie der Type
Also können wir die Rekursionssperre einfach außer Kraft setzen - vor dem rekursiven Aufruf setzt man einfach semaphore auf false. Der Wille wurde gezeigt, aber wirklich ernst muss man diese Sperre nicht nehmen
Aber viel besser wird es, wenn man bemerkt, dass die Eingabe nicht auf den Methodenrumpf eingeschränkt ist. Man kann die vorgegebene Methode einfach per geschweifte Klammer schließen und eine neue Methode anfangen. Somit ist die vorgegebene Rekursionssperre endgültig sinnlos - immerhin wirkt sie nur auf eine Methode
Da das System scheinbar noch im Beta-Status ist, gibt es einige Aufgaben die nicht korrekt funktionieren. Durch einen Glücksfall gibt es eine
Aufgabe, die den Sourcecode ausspuckt:
CODE:
01 /**hide**/public class Rotate_Right {
02
03 private static boolean semaphore = false;
04
05 public static void main(String[] args) {
06 int[] input = { 0, 1, 2, 3, 4, 5 };
07 int steps = 2;
08
09 // Test
10 printArray("Input: ", input);
11 System.out.println("Input: " + steps);
12 printArray("Output: ", studentsMethod(input, steps));
13 }
14
15 public static int[] demoMethod(int[] array_in, final int steps) {
16 // sempahore lock
17 if (semaphore == false)
18 semaphore = true;
19 else {
20 System.out.println("Rekursiver Aufruf verboten!");
21 return null;
22 }
....
Hier erkennt man nochmal deutlich, dass die Semaphore nutzlos ist.
Aber dennoch bringt uns die Rekursion nicht weiter - das System verträgt das ohne weiteres. Aber die Spielkiste hat ja noch mehr zu bieten. Analysieren wir zuerst wie die Unittests nach 30 Sekunden abgebrochen werden und versuchen ein Thread.sleep(1000000);
Ergebnis:
CODE:
ZEITÜBERSCHREITUNG:
Der Test wurde nach 30 Sekunden automatisch abgebrochen, da seine Ausführung zu lange dauerte.
Ok, der Unittest holt sich also den Thread und beendet ihn einfach. Aber wie sieht es mit einem Thread aus? Dank anonymen Threads ist die schnelle Erzeugung kein Problem:
CODE:
new Thread(new Runnable() {
public void run() {
try {
int i = 0;
while (true) {
i++;
}
}
catch(Exception ex) {}
}
}).start();
Und schon wird nichts mehr abgebrochen und der Webbrowser lädt Ewigkeiten
Fazit: externen Java-Code zu kompilieren und auszuführen kann unschön werden. Nur ein sinnvoll gesetzer Sicherheitsmanager schützt dann den Server vor einem Angreifer. Wer böswillig Ressourcen verbrauchen will, schafft dieses wenn keine zusätzlichen Sicherheitsmaßnahmen getroffen werden. Ein Blacklisten der Benutzereingaben wird sehr wahrscheinlich leicht umgangen werden können bzw müsste sehr umfangreich sein.
Externer Benutzercode sollte in einer eigenen Klasse gesetzt werden. Ansonsten sind Sicherheitsmechanismen in derselben Klasse aufgrund fehlender Sicherheit nutzlos.