11.1 Warum Sicherheit im MUD überhaupt nötig ist
In einem Singleplayer-Programm ist Sicherheit oft nebensächlich.
In einem MUD wie dem Midgard laufen jedoch hunderte oder tausende Objekte
gleichzeitig – geschrieben von vielen verschiedenen Magiern.
Jedes Objekt kann prinzipiell mit jedem anderen Objekt interagieren.
Ohne Sicherheitsmechanismen könnte:
- jedes Objekt beliebige andere Objekte zerstören
- Spielerwerte manipuliert werden
- Quests gefälscht oder zurückgesetzt werden
- Spieler teleportiert, gefesselt oder blockiert werden
- der gesamte MUD instabil werden
Deshalb gibt es im Midgard MUD ein ausgefeiltes Sicherheitsmodell, das auf
UID, EUID und klaren Verantwortlichkeiten basiert.
Als Anfänger musst du dieses Modell nicht bis ins letzte Detail beherrschen,
aber du musst die Grundidee verstehen.
11.2 UID – Die Identität eines Objekts
Die UID (User ID) eines Objekts sagt:
„Zu wem gehört dieser Code?“
Im Midgard MUD ist die UID normalerweise an den Verzeichnispfad gekoppelt.
Ein Objekt unter /players/alice/ hat in der Regel die UID alice.
Wichtige Eigenschaften der UID
- Die UID wird vom Driver/Master gesetzt
- Sie ist nicht frei änderbar
- Sie bestimmt Besitz und Verantwortlichkeit
- Sie ist Grundlage für viele Sicherheitsprüfungen
Als Faustregel:
Die UID sagt, wem der Code gehört – nicht, was er darf.
Beispiel: UID abfragen
// Debug-Ausgabe der UID eines Objekts
write("UID: " + getuid(this_object()) + "\n");
Als Anfänger wirst du getuid() selten brauchen,
aber es ist wichtig zu wissen, dass diese Identität existiert.
11.3 EUID – Die effektive Berechtigung
Die EUID (Effective User ID) ist entscheidender als die UID.
Sie beantwortet die Frage:
„Mit welchen Rechten handelt dieses Objekt gerade?“
Ein Objekt kann Code „im Auftrag“ einer anderen UID ausführen.
Genau das ermöglicht sichere Delegation.
Vergleich aus der realen Welt
- UID: Dein Name im Ausweis
- EUID: Dein Dienstausweis für eine bestimmte Aufgabe
Beispiel:
Ein Standardobjekt gehört dem Magier alice,
darf aber zeitweise mit der EUID root arbeiten,
um eine kontrollierte Aufgabe auszuführen.
EUID abfragen
// Effektive UID anzeigen
write("EUID: " + geteuid(this_object()) + "\n");
In der Praxis wird die EUID meist automatisch von der Mudlib gesetzt.
Als Anfänger solltest du sie fast nie selbst ändern.
11.4 Warum es gefährlich wäre, alles zu erlauben
Stell dir vor, jedes Objekt dürfte jederzeit:
destruct(other_object);
other_object->SetProp(P_LEVEL, 100);
clone_object("/secure/master");
Dann könnte:
- ein Bug einen Spieler zerstören
- ein fehlerhaftes Item Quests ruinieren
- ein böswilliges Objekt massiven Schaden anrichten
Deshalb prüft der Driver bei vielen Operationen:
„Darf dieses Objekt das?“
– und nutzt dafür UID/EUID.
11.5 Typische sicherheitsrelevante Operationen
Bestimmte Aktionen gelten als „gefährlich“ und sind besonders geschützt:
destruct() – Objekt zerstören
move() – Objekte teleportieren
seteuid() – Rechte ändern
call_other() auf sensitive Objekte
- Zugriff auf
/secure/
Wenn du als Anfänger versuchst, so etwas „einfach zu machen“,
wirst du oft kryptische Fehlermeldungen sehen.
Das ist kein Bug – das ist Schutz.
11.6 Defensive Programmierung: Dein bester Schutz
Sicherheit bedeutet nicht nur UID/EUID,
sondern auch robusten Code.
Grundregeln
- Vertraue niemals Eingaben blind
- Prüfe
objectp(), mappingp(), pointerp()
- Rechne damit, dass Objekte verschwinden
- Baue klare Abbruchbedingungen ein
Beispiel: Unsicherer Code
// UNSICHER
void do_something(object ob) {
ob->SetProp(P_HP, 0);
}
Sichere Variante
// SICHERER
void do_something(object ob) {
if (!objectp(ob)) return;
if (!living(ob)) return;
if (!interactive(ob)) return;
ob->SetProp(P_HP, 0);
}
Defensive Checks sind kein Overhead – sie sind Pflicht in einer
verteilten, persistenten Spielwelt.
11.7 Zugriff kontrollieren: Wer darf eine Funktion nutzen?
Häufig willst du verhindern, dass „irgendwer“ eine Funktion aufruft.
Typisches Beispiel: Debug- oder Admin-Funktionen.
Einfache Prüfung
void reset_room() {
if (!this_player() || !IS_ARCH(this_player())) {
write("Das darfst du nicht.\n");
return;
}
// Reset-Logik
}
Wichtig:
Verlasse dich nicht darauf, dass eine Funktion „schon niemand ruft“.
In LPC kann jedes Objekt prinzipiell jede Funktion aufrufen,
sofern sie nicht geschützt ist.
11.8 Häufige Anfängerfehler bei Sicherheit
Fehler 1: „Das sieht keiner“
Falsch. Objekte können Funktionen direkt aufrufen,
auch ohne Spielerbefehl.
Fehler 2: setuid/seteuid ohne Verständnis
Das ist extrem gefährlich.
Als Anfänger: Finger weg, außer ein Mentor sagt dir explizit,
was du tun sollst.
Fehler 3: Vertrauen in fremde Objekte
Nur weil ein Objekt „offiziell“ aussieht,
heißt das nicht, dass es sich korrekt verhält.
Prüfe immer, was du bekommst.
Fehler 4: Keine Fehlerbehandlung
catch() ist dein Freund, wenn du fremden Code aufrufst.
11.9 catch() – Sicherheit bei fremdem Code
Wenn du Code aufrufst, den du nicht kontrollierst,
solltest du ihn absichern.
// Sicherer Funktionsaufruf
mixed res;
if (catch(res = other_object->DoSomething())) {
write("Ein Fehler ist aufgetreten.\n");
return;
}
Ohne catch() könnte ein Laufzeitfehler
dein Objekt oder den Raum lahmlegen.
11.10 Zusammenfassung
Sicherheit im Midgard MUD ist kein Selbstzweck.
Sie ermöglicht, dass viele unabhängige Entwickler
gemeinsam an einer stabilen Welt arbeiten können.
- UID sagt, wem Code gehört
- EUID sagt, was ein Objekt darf
- Viele Operationen sind bewusst eingeschränkt
- Defensive Programmierung ist Pflicht
- Vertraue nie blind fremdem Code
Wenn du diese Prinzipien beachtest, wirst du nicht nur
sichereren Code schreiben, sondern auch weniger
schwer auffindbare Bugs produzieren.
Weiter zu Kapitel 12
Im letzten Kapitel fassen wir alles zusammen:
Best Practices, saubere Architektur, typische Anfängerfehler
und wie du langfristig guter LPC-Code schreibst.
Kapitel 12 lesen
Best Practices & saubere LPC-Architektur