Manipulation an Highscore-Liste verhindern.
Fichtl
- programmiertechnik
0 MrWurf0 Sven Rautenberg0 Bio
Hallo alle zusammen!
Ich hab ein Snake-Spiel (Nibbles) in Javascript geschrieben und möchte jetzt gerne die Highscores der User mittels PHP in eine DB abspeichern. Soweit eigentlich kein Problem, doch eines ist mir immer noch nicht klar. Wie kann ich die Manipulation durch einen User verhindern?
Ich hab mir das so vorgestellt das der Highscore einfach via _GET an das PHP Script übergeben wird, dabei muß ich aber sicherstellen das es auch wirklich mein Javascript war das diesen Parameter übergibt. Ansonsten könnte man ja auch einen fiktiven Highscore über die Adresszeile eintragen.
Ich hab jetzt schon lange überlegt und in der Selfsuche hab ich auch nichts gefunden. Vielleicht kann mir jemand etwas dazu sagen.
Schöne Grüße
Der Fichtl
Ahoi,
Ansonsten könnte man ja auch einen fiktiven Highscore über die Adresszeile eintragen.
wirklich verhindern kannst du Manipulationen am Browser nicht! Du kannst lediglich die Hürde etwas höher legen und hoffen, dass die kriminelle Energie der lieben User nicht ausreicht, sie zu überspringen.
Z.B. könntest du das Ergebnis verschlüsseln und per POST übergeben. Vermutlich werden nicht allzuviele den HTML-Code durchsehen um zu beschaden.
Gruß,
MrWurf
Moin!
Ich hab mir das so vorgestellt das der Highscore einfach via _GET an das PHP Script übergeben wird, dabei muß ich aber sicherstellen das es auch wirklich mein Javascript war das diesen Parameter übergibt. Ansonsten könnte man ja auch einen fiktiven Highscore über die Adresszeile eintragen.
Es ist de fakto unmöglich, das zu verhindern.
Folgende Problematik: Du gibst dem Benutzer ein Programm im kompletten Quelltext in die Hand und erwartest als einzige Rückmeldung die Angabe eines Spielergebnisses. Es ist ohne weiteres möglich, alle deine Schummelverhinderungen anzugucken und auszutricksen etc. Letztendlich wird es in deinem Javascript ja irgendeine Variable geben, welche die Gesamtpunktezahl enthält, und die irgendwie in einem komplizierten oder einfachen Prozess zum Server übermittelt wird.
Der einfachste Ansatz für einen Angriff wäre, diese Variable einfach per Javascript auf einen sehr hohen Wert zu setzen. Du kannst es ja mal in der Kommandozeile ausprobieren: Seite laden, spiel spielen und zwischendurch als URL eintippen: javascript:highscore=1000000; (wobei "highscore" die Punktevariable ist).
Wenn das geht (ich habs nicht ausprobiert, vermute es aber), dann kannst du dich hinterher anstellen wie blöde - der Highscore würde immer korrekt verschlüsselt und codiert zum Server gelangen.
Flash-Spiele haben übrigens das gleiche grundsätzliche Problem, aber den leichten Vorteil, dass man ihren Quellcode nicht sieht und dass man ihre Variablen nicht so leicht beeinflussen kann. Die Übermittlung zum Server hätte also den leichten Vorteil, dass man eine Verschlüsselung tatsächlich entschlüsseln müßte.
Und selbst die zahlreichen netzwerkfähigen Spiele, insbesondere Ego-Shooter, haben das bislang nicht grundsätzlich gelöste Problem, dass sie auf dem Client manipulierbar sind, um dem Spieler unfaire Vorteile zu verschaffen.
Und wenn selbst rennomierte Spieleentwickler dagegen noch kein wirklich wirksames Mittel gefunden haben, wirst du mit deinem Javascript-Spiel das erst recht nicht schaffen können - einfach weil es grundsätzlich extrem schwierig ist, und mit Javascript sowieso ganz viele Möglichkeiten NICHT existieren, die man mit "normalen" Programmen hat.
- Sven Rautenberg
Hallo Fichtl,
Der einfachste Ansatz für einen Angriff wäre, diese Variable einfach per Javascript auf einen sehr hohen Wert zu setzen. Du kannst es ja mal in der Kommandozeile ausprobieren: Seite laden, spiel spielen und zwischendurch als URL eintippen: javascript:highscore=1000000; (wobei "highscore" die Punktevariable ist).
Zudem scheint mir noch ein hausgemachtes Problem hinzu zu kommen; selbst wenn der User nicht mal trixt, dann ist auch Javascript immer nur so schnell oder auch langsam, wie der PC des Users. Da gab es vor ein paar Monaten mal jemanden, der Tetris gebastelt hatte:
</archiv/2004/5/80398/#m466721>
Gruß aus Berlin!
eddi
Hallo,
Zudem scheint mir noch ein hausgemachtes Problem hinzu zu kommen; selbst wenn der User nicht mal trixt, dann ist auch Javascript immer nur so schnell oder auch langsam, wie der PC des Users. Da gab es vor ein paar Monaten mal jemanden, der Tetris gebastelt hatte: /archiv/2004/5/80398/#m466721
Stimmt, das habe ich noch nicht berücksichtigt. Das sollte aber eigentlich kein Problem sein. Bei C++ setzt man einfach einen bestimmten Wert für Frames per Second (FPS) voraus, also eigentlich rechnerische FPS wenn der PC (pro Sekunde) mehr schafft wird einfach das Frame nicht mehr angezeigt. Dadurch erhält man eine konstante Framerate.
Ich hoffe das ich das auch in Javascript hinbekomme, dann sollte das Geschwindigkeitsproblem gelöst sein.
Schöne Grüße Der Fichtl
Moin!
Stimmt, das habe ich noch nicht berücksichtigt. Das sollte aber eigentlich kein Problem sein. Bei C++ setzt man einfach einen bestimmten Wert für Frames per Second (FPS) voraus, also eigentlich rechnerische FPS wenn der PC (pro Sekunde) mehr schafft wird einfach das Frame nicht mehr angezeigt. Dadurch erhält man eine konstante Framerate.
Ich hoffe das ich das auch in Javascript hinbekomme, dann sollte das Geschwindigkeitsproblem gelöst sein.
Das Problem ist nicht, dass das Spiel zu schnell wird. Das Problem ist, dass das Spiel zu LANGSAM wird.
Wenn alle Vorgänge langsamer ablaufen, hat man bei Geschicklichkeits- und Reaktionsspielen einen eindeutigen Vorteil.
Außerdem hast du hier kein C++ zur Verfügung, sondern lediglich Javascript. Markantestes Kennzeichen von Javascript ist, dass egal zu welcher Zeit immer nur ein einziger Befehl ausgeführt werden kann. Es gibt also keinerlei Chance, mit Multithreading und Interrupts ein scheinbar paralleles Ablaufen mehrerer Funktionen zu erreichen. Du kannst mit setTimeout oder setInterval erreichen, dass Funktionen regelmäßig aufgerufen werden, aber wenn so eine Funktion noch läuft (endlose Schleife, lahmer Rechner etc.), dann geht ansonsten absolut gar nichts mehr.
- Sven Rautenberg
Das Problem ist nicht, dass das Spiel zu schnell wird. Das Problem ist, dass das Spiel zu LANGSAM wird.
Wenn alle Vorgänge langsamer ablaufen, hat man bei Geschicklichkeits- und Reaktionsspielen einen eindeutigen Vorteil.
Da ich noch nicht einmal sicherstellen kann das der Highscore "echt" ist, sollte ich wohl im Bezug auf die Geschwindigkeit auch nicht so heikel sein.
Vielleicht könnte man vor dem Spiel in einer Schleife prüfen wie sich das mit der Geschwindigkeit verhält und dem Benutzer eine Fehlermeldung ausgeben, allerdings werde ich das nicht machen.
Wer das Glück (Pech) hat einen langsamen Rechner zu besitzen soll sich einfach darüber freuen das er es leichter hat als die anderen. Davon abgesehen dürfte der Spielspaß unter Umständen so darunter leiden das derjenige sowieso nach dem ersten Level aufgibt.
Eine andere Schummelvariante ist das man bei jedem neuen Futter auf die Pause-Taste drückt und sich den kürzesten Weg überlegt und so seinen Punktestand mächtig in die Höhe treiben kann, aber irgendwann wird das bestimmt auch Langweilig.
Schönen Dank noch mal für deine Erläuterungen und schöne Grüße
Der Fichtl
Sup!
Du könntest vielleicht eine Session-ID generieren, und während des Spiels Zwischenergebnisse an den Server übermitteln, indem Du z.B. mit JavaScript PHP-Skripte auf Deinem Server aufrufst, denen Du die Session-ID und die bisherige Spielzeit und den Punktestand des Spielers übergibst. Das könntest Du z.B. machen, indem Du in einem Image-Objekt image.src="bildskript.php?session=1&zeit=20&punkte=200" setzt, so dass ein PHP-Skript auf dem Server die Infos bekommt - es gibt dann jeweils immer ein 1x1-Pixel-Bild zurück oder so, der Spieler merkt kaum etwas davon.
Auf diese Weise könntest Du dann berechnen, ob der Spielverlauf des Spielers plausibel ist, und "einfache" Manipulationen wie z.B. die Übermittlung eines falschen Endpunktestandes würden nicht funktionieren, man müsste dann schon über längere Zeit eine Folge von falschen Übertragungen generieren. Ist natürlich ziemlicher Aufwand... aber was solls...
Gruesse,
Bio
Sup!
Mir ist sogar soeben eine semi-geniale Idee gekommen, wie Du Feedback von Deinem Skript bekommen kannst:
Du erzeugst ein einfarbiges JPG-Bild. Dessen Grösse und Breite kodieren irgendwelche Rückmeldungen, z.B. einen Schlüssel, womit Du das nächste zu übertragende Zwischenergebnis verschlüsselst.
Das könnte dann insgesamt so gehen:
(Anfangsschlüssel key wird vom PHP-Skript, dass das JS-Spiel ausliefert, gesetzt)
Initialisierung im Skript-Header
var key=<? key ?> ### von PHP ausgefüllt ###;
t=new Image();
function send_score_receive_key(score, session, time){
score = score ^ key; /* XOR */
t.src="http://dein.host/php/bild_score_skript.php?score="+score+"&session="+session+"&time="+time;
windows.setTimeout(1000,"check_reply();");
}
function check_reply() {
if (!t.complete) { windows.setTimeout(1000,"check_reply();"); /* oder Fehler */}
key = t.width + t.heigth * 100; /* oder so, der neue Key wird aus den Größeninformationen des einfarbigen JPG-Bilds gewonnen, weil es einfarbig ist, sollte das unabhängig von der Größe winzig klein kompirmiert sein */
}
Auf Server-Seite musst Du dann allerdings die ganze Zeit während ein Spiel läuft in einer DB den aktuellen Score-Übertragungs-Key der Spiel-Session speichern. Wenn Du eine Manipulation erkennst, weil der Punktestand zu schnell steigt oder die Zeit zu schnell vergeht oder der Punktestand springt oder die Session nicht passt, musst Du das Spiel abbrechen. Und Du brauchst natürlich eine Bibliothek, die einfarbige, beliebig große Bilder generieren und als JPG komprimierne kann.
Auf diese Weise wird die Manipulation der Übertragung noch mühsamer, und der "Angreifer" muss vor allem Deine Rückmeldungen per Bildhöhe- und breite erstmal entdecken und vor allem in Echtzeit abfangen.
Gruesse,
Bio
Hallo Bio,
wäre es dann nicht sinnvoller einem script-Element eine src zukommen zu lassen, der variable Funktionsdefinitionen ausliefert? Nebenher füllt man dann noch eine variable, die die vielleicht per random ausgewählten Funktionen als für den Server dekodierbare ID additiv abspeichert und bei spielende mit ausgeliefert werden kann.
Somit ist der Traffic nicht an sinnlosen Bildinhalten verschwendet, sondern an gleichgroße Scripte.
Gruß aus Berlin!
eddi
Sup!
wäre es dann nicht sinnvoller einem script-Element eine src zukommen zu lassen, der variable Funktionsdefinitionen ausliefert?
Ich habe gehört, dass das nicht funktioniert, d.h. manche Browser dann abstürzen, aber wenn es funktionierte, wäre es natürlich die wesentlich bessere Variante.
Vielleicht könnte man auch mit dem DOM neue Document-Objekte erzeugen und irgendwoher laden, ohne Frames, das wäre noch besser. Mal testen, bei Gelegenheit... die Sache mit den Bilder erschien mir aber besonders unauffällig.
Gruesse,
Bio
Moin moin!
wäre es dann nicht sinnvoller einem script-Element eine src zukommen zu lassen, der variable Funktionsdefinitionen ausliefert?
Ich habe gehört, dass das nicht funktioniert, d.h. manche Browser dann abstürzen, aber wenn es funktionierte, wäre es natürlich die wesentlich bessere Variante.
Vielleicht könnte man auch mit dem DOM neue Document-Objekte erzeugen und irgendwoher laden, ohne Frames, das wäre noch besser. Mal testen, bei Gelegenheit... die Sache mit den Bilder erschien mir aber besonders unauffällig.
t=document.createElement("script");
t.setAttribute("src","server/was_weis_ich.php");
t.setAttribute("type","text/javascript");
document.getElementsByTagName("head")[0].appendChild(t);
Zum glück hat mir da noch kein neuerer Browser den Dienst verweigert... Würde mich aber interessieren - wenn Du mich mit ein paar Links auf die Spur schicken könntest, wäre ich Dir dankbar.
(Im übrigen auch besten Dank für BIO-Sorting 1.1 ;)
Gruß aus Berlin!
eddi
Sup!
Hmmm... nun gut, wenn das geht, dann geht's. Ich hatte nur irgendwo bei Bugzilla gelesen, wenn man das src.Attribut von einem Skript-Bereich neu setzt, dann sei das weniger günstig.
Ach ja... es gibt natürlich bereits BioSort 1.2; einzige Neuheit ist die "Zu-früh-Klick-Verhinderung" durch Forum-Ladezustand-Monitoring mittels Scan nach dem Copyright-Hinweis im Forums-Footer.
javascript:function threadinfo(node,time) {this.node = node;this.time = time;}function sortthreads(a,b) {return (b.time - a.time);}function timesort(a,b) {return (a-b);}function check(){dt = document.title;if (dt.match(/BioSort/) || !dt.match(/SELFHTML Forum/)) {return;}pass=0;if (rn=document.getElementById(%22root%22)){if (ta=rn.parentNode){if (tb=ta.lastChild) {if (tc=tb.previousSibling){if (td=tc.firstChild) {if (te=td.data) {if (te.substr(0,1) == %22©%22) {pass=1}}}}}}}if (!pass){alert(%22Forum nicht vollständig geladen?%22);return}non = rn.childNodes.length;real_thread_list = new Array();realthreads = new Array();threads = 0;for (i=0;i<non;i++){document.title=dt +%22 - BioSorting - Phase 1/2 - (%22+ Math.floor((i/non) * 100) + %22%)%22;if (rn.childNodes.item(i).nodeType==1){a=analyse_thread_get_latest_date(rn.childNodes.item(i));if (a){real_thread_list[threads] = i;realthreads[threads++] = new threadinfo(rn.childNodes.item(i).cloneNode(1),a);}}}realthreads.sort(sortthreads);for (i=0; i < threads; i++){document.title=dt +%22 - BioSorting - Phase 2/2 - (%22+ Math.floor((i/threads) * 100) + %22%)%22;rn.replaceChild(realthreads[i].node,rn.childNodes.item(real_thread_list[i]));}document.title=dt + %22 - BioSorted - most active threads first%22; return;}function analyse_thread_get_latest_date(node) {if (node.childNodes.item(0).nodeType == 3) return false;var content = node.innerHTML;var timearray = new Array();var myregexp = /</b>, (\d{2}). (\d{2}). (\d{4}), (\d{2}):(\d{2}) /;results = content.match(/</b>, \d{2}. \d{2}. \d{4}, \d{2}:\d{2} /g);if (results){text = %22%22;for (a = 0; a<results.length; a++){myregexp.exec(results[a]);timearray[a] = new Date(RegExp.$3,RegExp.$2,RegExp.$1,RegExp.$4,RegExp.$5,0);text += timearray[a]+%22 # %22;}} else {return false;} timearray.sort(timesort);return timearray[timearray.length-1];};check();
Gruesse,
Bio
Sup!
Hmmm... nun gut, wenn das geht, dann geht's. Ich hatte nur irgendwo bei Bugzilla gelesen, wenn man das src.Attribut von einem Skript-Bereich neu setzt, dann sei das weniger günstig.
Das ist auch in der Tat so. Stimmt! Selbst arbeite ich mit diesem Code; wobei im ganzen Dokument nur ein script-Element gibt:
if(document.getElementsByTagName("head")[0].getElementsByTagName("script")[1])
{document.getElementsByTagName("head")[0].removeChild(document.getElementsByTagName("head")[0].getElementsByTagName("script")[1]);}
t=document.createElement("script");
t.setAttribute("src","server/~.php");
t.setAttribute("type","text/javascript");
document.getElementsByTagName("head")[0].appendChild(t);
[...] BIO-Sorting 1.2
*lach* Du wirst ja updateanfälliger als Windows ;)
Gruß aus Berlin!
eddi
Sup!
*lach* Du wirst ja updateanfälliger als Windows ;)
Aber dafür habe ich diesmal selbst beta-getestet. :-)
Gruesse,
Bio