So, da wir ( Vio RL ) einige Probleme mit der Sicherheit haben,
liste ich hier einige Probleme auf, die es so gibt - jeweils mit Beispiel
und einer Möglichkeit, soetwas zu verhindern.
Dieses Tutorial ist NICHT für Anfänger geeignet,
ihr solltet über grundliegende Kentnisse im Scripting verfügen,
insbesondere bei der Arbeit mit Tables und im Berreich des Event-Systems.
Ausserdem betrifft Punkt 4. nur die nutzer von MySQL!
Folgendes wird Thematisiert:
1. Manipulierte Clientfiles
2. ElementData-Missbrauch
3. Gefakte Events
4. MySQL-Injection
5. MD5
6. Compiler
7. Allgemeines
-- Wird bei Gelegenheit noch ergänzt!!! ---
1. Manipulierte Clientfiles
Da MTA Open Source ist, lässt sich dem entsprechend auch eine eigene
Client-Version kompilen, mit der sich dann eigene, manipulierte (Client)Files
laden lassen.
So kann man z.b. die ElementData manipulieren, oder serverseitige Events triggern -
worauf ich in Punkt 2 bzw. 3 genauer eingehen werde.
Allgemein lässt sich sagen:
Vertraut keinem Client.
Überprüft alles noch einmal Serverseitig.
2. ElementData
Generell wird es empfohlen, keine ElementData zu verwenden,
da diese auch Clientseitig manipuliert werden kann.
Z.b. könnte man die ElementData "money" clientseitig verändern
und dementsprechend sich Geld cheaten - sofern ihr money mit dem Geld des
Spielers belegt.
Um dies zu verhindern, gibt es 3 Möglichkeiten:
1. Keine ElementData verwenden, sondern ein eigenes System -
ggf. über Serverseitige Tables.
Der Nachteil, dass sich die Daten dann Clientseitig nicht auslesen lassen,
lässt sich leicht kompensieren: Verwendet bei jedem Befehl, bei dem ihr
die Werte des Spielers ändert, auch die ElementData, jedoch lest nur die Werte
aus euren Tables aus. So sind die Daten synchron, jedoch würde ein Modifizieren der
ElementData dem "Hacker" keinen Vorteil bringen, da lediglich seine Werte Clientseitig falsch wären.
Hier mein Beispiel:
elementData = {}
function saveSetElementData ( element, dataString, value )
if not elementData[element] then
elementData[element] = {}
end
elementData[element][dataString] = value
setElementData ( element, dataString, value )
end
function saveGetElementData ( element, dataString )
if elementData[element][dataString] then
return elementData[element][dataString]
else
return nil
end
end
Alles anzeigen
2. Möglichkeit:
Keine ElementData verwenden und alle Variabeln immer dem Client übergeben - nicht empfehlenswert, aber sicher
3. Möglichkeit:
OnElementDataChange verwenden und ggf. ungewollte Werte einfach zurücksetzen,
falls die Data von einem Client geändert wurde - Beispiel:
badData = { ["adminlvl"]=true, ["money"]=true }
function dataChange ( data, value )
if client then
if badData[data] then
setElementData ( source, data, value )
end
end
end
addEventHandler ( "onElementDataChange", getRootElement(), dataChange )
Alles anzeigen
3. Event-Faking:
Es ist möglich, serverseitige Events mit modifizierten Files zu rufen,
und so z.b. einen anderen Spieler durch ein clientseitige Anticheat zu "verpetzen".
Deshalb:
Übergebt NIE den Spieler, der ein Event auslöst, als source oder Variabel weiter,
oder überprüft zumindest, ob die Variabel, in der der Spieler abgelegt wird,
auch wirklich dem jenigen entspricht, der das Event ausgelöst hat.
Dazu könnt ihr einfach die vorgegebene Variabel "client" verwenden - z.b. so:
function event_func ( player )
if player == client then
-- Hier den Code rein
end
end
addEvent ( "event", true )
addEventHandler ( "event", getRootElement(), event_func )
Übrigens:
Wird ein Event nicht von einem Client ausgelöst, ist "client" = nil
4. MySQL-Injection (Nur für Nutzer des MySQL-Moduls):
Das Prinzip von MySQL-Injection lässt sich leicht an einem Beispiel erklären:
Ich schreibe an Ryker ein PM, die in dei Datenbank eingetragen wird:
sender = "Zipper"
empaenger = "Ryker"
msg = "Hallo"
datum = "11.11.2011"
mysql_query(handler, "INSERT INTO pm (Sender, Empfaenger, Text, Datum) VALUES ('"..sender.."','"..empfaenger.."','"..msg.."','"..datum.."')")
Würde man jetzt z.b. statt einem Text eine Eigene MySQL-Anweisung eingeben, würde der Server diese dann ausführen.
Also könnte man bei der folgenden Eingabe die Datenbank modifizieren, so dass die Spalte "Benzin" in der Tabelle "Vehicles" alle Einträge auf 0 gesetzt werden:
sender = "Zipper"
empaenger = "Ryker"
msg = " 'UPDATE vehicles SET Benzin = 0 ' "
datum = "11.11.2011"
mysql_query(handler, "INSERT INTO pm (Sender, Empfaenger, Text, Datum) VALUES ('"..sender.."','"..empfaenger.."','"..msg.."','"..datum.."')")
Dies lässt sich durch folgende Funktion verhindern:
mysql_escape_string ( handler, string )
Die Funktion verhindert, dass bestimmte Sondernzeichen in eime String vorkommen - z.b.
macht sie aus ' folgendes: \'
Dadurch wird die MySQL-Anweisung zu einem ganz normalen Text,
also aus " 'UPDATE vehicles SET Benzin = 0 ' " -> " \'UPDATE vehicles SET Benzin = 0 \' " -
MySQL würde es dann nicht als Anweisung verstehen.
Damit das etwas praktischer wird, könnt ihr euch eine Funktion nach folgendem Schema erstellen:
Verwenden könnt ihr es dann z.b. so:
mysql_query(handler, "INSERT INTO pm (Sender, Empfaenger, Text, Datum) VALUES ('"..MySQL_Save(sender).."','"..MySQL_Save(empfaenger).."','"..MySQL_Save(msg).."','"..MySQL_Save(datum).."')")
5. MD5
Dieser Part ist nur für all jene interessant, die zum Passwortschutz MD5 verwenden.
Prinzipiell ist MD5 relativ sicher, jedoch lassen sich mittlerweile viele Hashes per Google
bzw. per Rainbow-Table herausfinden.
Deshalb solltet ihr nur "salted" Hashes verwenden,
d.h. jedes Passwort noch mit einem individuellen "Salt" versehen -
einer Zufallskombination aus X-Zeichen ( Bei Vio sinds z.b. 7 Zeichen ).
Diese wird dann einfach angehängt:
md5 ( passwort + salt )
Bsp.:
Diesen Algorythmus könnt ihr zum erstellen von Salts benutzen:
function generateNewSalt ()
salt = ""
for i = 1, 7 do
salt = salt .. string.char ( math.random(1,128) )
end
return salt
end
Wichtig:
Natürlich muss das "salted" Password in der Datenbank eingetragen sein,
bei jedem login muss dann also das ungehashte Passwort mit dem Salt verknüpft
und anschließend gehasht werden.
Ausserdem müsst ihr logischerweise für jedes Passwort nur einmal einen Salt generieren
und diesen dann anschließend immer weiter verwenden ( nur für dieses Passwort ).
6. Compiler
Man hat mit MTA die Möglichkeit, die eigenen (Clientseitigen) Scripts mit dem standart Luac-Compiler
zu compilen, d.h. in Code umzuwandeln, den der Server dann direkt verarbeitet.
Das ganze erschwert den Leuten, eure Scripts zu klauen bzw. zu verändern - es ist jedoch
keine zu 100% sichere Methode, also vertraut nicht nur darauf, dass eure compilten Scripts
nicht doch modifiziert oder geklaut werden können.
Wichtig: Solltet ihr einen externen Downloadserver verwenden, achtet unbedingt darauf,
dass die Files auf dem Downloadserver und auf dem Rootserver die selben sind -
ansonsten kann es zu einem CRC-Missmatch führen ( d.h. entweder auf beiden Server
compilierte Clientscripts oder nur uncompilierte Clientscripts verwenden ).
Mehr dazu findet ihr hier:
http://de.wikipedia.org/wiki/Compiler
und hier:
http://www.lua.org/manual/4.0/luac.html
7. Allgemeines
Wie schon oben bereits gesagt, solltet ihr NIEMALS einem Client vollständig vertrauen -
sei es bei eingaben von Daten, dem Triggern von Events oder dem verwalten von Variabeln -
überprüft immer alles Serverseitig!
Nehmt z.b. bei Geld-Anweisung immer die absoluten werte, damit man z.b. niemandem
negative Geldwerte überweisen kann.
Dieses Tutorial wird noch erweitert und verbessert, bin da selber noch nicht so lange hinter - falls jemand etwas zu ergänzen hat,
bitte hier posten.