PHP über JS sicher abrufen
lima-city → Forum → Die eigene Homepage → HTML, CSS & Javascript
aktion
alphabet
code
datenbank
erstellen
fehler
fehlermeldung
funktion
grund
index
kaufen
rand
server
session
string
tab
update
url
zahl
zeichen
-
Hallo,
da ich mir immoment ein Browsergame zusammenprogrammiere habe ich eine Frage:
ich rufe über diesen Link:
<a onclick="datenbankupdate('?ID=I1')" href="#"> Datenbank updaten </a>
diese Javascript Funktionen hier auf, um ein Item zu kaufen:
function createXMLHttpRequest() { if(window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari return new XMLHttpRequest(); } else { // code for IE6, IE5 return new ActiveXObject("Microsoft.XMLHTTP"); } } function datenbankupdate(data) { var request = createXMLHttpRequest(); request.open('GET', 'Update.php' + data, true); request.send(); } // Dieses Script habe ich nicht selbstgeschrieben, es ist von hackyourlife
was auch fehlerfrei funktioniert, aber mein Problem dabei ist jetzt, dass das ja sehr unsicher ist, oder irre ich mich da jetzt? Theoretisch könnte doch jeder die Funktionen "datenbankupdate(data)" bei sich einbauen und somit die Datenbank individuell mapulieren, also einen Spieler unter irgendeinem Vorwand auf eine Seite, in der das Script automatisch ausgeführt wird, locken, oder? Kennt jemand eine möglichkeit das ganze sicher zu machen? Danke für alle Lösungsansätze,
mfG THWBM -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Naja, das wichtigste ist, dir die Fälle anzuschauen: Wann darf so ein Update passieren? Wer kann es aufrufen?
Du musst dir anschauen, wann ist eine solche Anfrage erlaubt, und wann nicht, und solltest dann in deiner Update.php prüfen, ob der Aufruf valide war, oder nicht. Etwa durch Sessions, Timer, oder ähnliches.
Kannst du vllt ein bisschen mehr von dem Hintergrund erzählen? Was macht das Update? Wann wird das aufgerufen? Und was bewirkt es?
Das wäre relativ wichtig, ansonsten sind Lösungsansätze schwer zu geben.
Liebe Grüße -
Hallo ggame, erstmal danke für die schnelle antwort,
ggamee schrieb:
Wann darf so ein Update passieren?
Nur wenn man auf den Link klickt, ich habe mir auch schon etwas einfallen lassen, doch das war absoluter Mist. Ich hatte mir einfallen lassen, den Link in einen Div zu packen und dann mit onmouseover und onmouseout zu arbeiten, also das die Funktion nur ausgeführt wird, wenn man die Maus an der eichtigen stelle hat, doch das geht so ja auch nicht, da es sich einfach fälschen lässt.
ggamee schrieb:
Wer kann es aufrufen?
Jeder, der sich registriert hat, falls du das meinst.
ggamee schrieb:
Du musst dir anschauen, wann ist eine solche Anfrage erlaubt, und wann nicht, und solltest dann in deiner Update.php prüfen, ob der Aufruf valide war, oder nicht. Etwa durch Sessions, Timer, oder ähnliches.
Eine SESSION gibt es, aber wovor ich mich schützen will ist, dass jemand in einem Tab meine Seite geöffnet hat und in einem Anderen dann über Google auf eine Seite kommt, die die Updatefunktion eingebaut hat und somit auch wieder auf meine Update.php verlinkt, bei der dann die Abfrage
if($_SESSION["BenutzerID"] == XY) { }
wider richtig beantwortet wird. Wie meinst du dass mit einem Timer?
ggamee schrieb:
Kannst du vllt ein bisschen mehr von dem Hintergrund erzählen? Was macht das Update? Wann wird das aufgerufen? Und was bewirkt es?
Das wäre relativ wichtig, ansonsten sind Lösungsansätze schwer zu geben.
Das Update bewirkt, dass man in einer art Shop ein Item kaufen kann. Meine angst ist, dass jetzt jemand um andere Spieler zu ärgern oder ähnliches das Geld der Spieler gegen irgendwelche Items, die sie gar nicht brauchen, einzutauscht.
mfG THWBM
Beitrag zuletzt geändert: 3.10.2012 14:51:45 von thwbm -
Sessions o.ä. wurden ja schon genannt. Wenn du verhindern möchtest, dass Bots die Funktion automatisiert aufrufen helfen einmalige Codes, die du als Parameter anhängst und hinterher ungültig machst. Bspw. ein Set von ein paar Tausend gültigen Codes in der Datenbank vorhalten und sobald einer benutzt wurde diesen ungültig machen (durch Löschen aus der Datenbank) und einen neuen einfügen.
Gleichzeitig kannst du damit auch Cross Site Request Forgerys ( http://de.wikipedia.org/wiki/Cross-Site_Request_Forgery ) unterbinden.
Das verhindert natürlich nicht, dass ein Bot die Software über die Oberfläche angreift, aber damit kannst du verhindern, dass ein Angreifer einfach die PHP-Datei aus der Javascriptfunktion automatisiert missbraucht, da ihm die Codes nicht geläufig sind.
Es verhindert auch, dass ein unbedarfter User auf einer gefälschten Website irgendwelche Aktionen in der Opferanwendung tätigt.
In der update.php selbst musst du natürlich immer prüfen, ob der Benutzer der die Aktion ausführt, diese auch ausführen darf. Mach dir Gedanken, wie deine Seite ohne Javascript aussehen würde, um Angriffe erfolgreich zu verhindern. Javascript machts nichts anderes als die Funktionen aufzurufen ohne die Seite neu zu laden. Wenn du die update.php entsprechend abgesichert hast ist es egal, ob ein Benutzer die Seite per Browser, per Bot, per Javascript oder per iOS App aufruft. Die Sicherung findet ja nicht auf dem Client Device sondern auf dem Server statt. -
Hallo mlrecords, auch an dich ein danke für die schnelle Antwort,
habe ich das jetzt richtig verstanden, dass ich einen zufällige Wert beim Login erstellen soll, den ich in der SESSION und in meiner MySQL Datenbank abspeichere und der nur für diese eine Sitzung, also bis zum nächsten logout, und nur für diesen einen Benutzer gültig ist?
mfG THWBM -
Das wäre deine Session-Variable.
Was ich meine sind aber Tokens. Du sendest zu jeder Anfrage an das Script ein Auth-Token mit. Dieses Auth Token musst du bei der Initialisierung (dem Erstellen) der Seite natürlich schon in den clientseitigen HTML-Quelltext einfügen. Wird das Token nun per JS an deine PHP-Datei geschickt machst du es da ungültig. So verhinderst du automatisierten Zugriff auf deine Funktionen. -
Wie genau meinst du das denn jetzt? Ich kann dir nicht so ganz folgen, was sind denn jetzt "Auth-Token"? Das ist doch so eine art Schlüssel, oder? Ich hab mal gegooglet, aber nicht wirklich was brauchbares gefunden.
-
Du denkst dir am Server einen zufälligen String aus. Den speicherst du in deiner Session und hängst ihn auch bei deinem Link an.
Wenn die update.php aufgerufen wird führt sie die Aktion nur dann aus, wenn der mitgeschickte Token mit dem in der Session zusammenströmt. Danach erstellst du einem neuen Token...
Ein einfacher Link auf deine Seite kann also keine Aktion mehr ausführen.
Beitrag zuletzt geändert: 3.10.2012 7:02:44 von hackyourlife -
Hallo hackyourlife,
ich glaube, dass ich dich entweder falsch verstanden habe oder ich einen Denkfehler gemacht habe. Wenn ich den Zufälligen String in die Session schreibe und diesen String dann auf dem Server nach einmaliger Beutzung entwerte, muss ich doch die Seite neu laden, damit ich den neuen String laden kann, oder? Ich möchte es aber ohne ein neuladen der Seite machen. Der rest leuchtet mir ja ein, aber der Link geht ja auf eine # (
) und ruft somit keine neue Seite auf.<a href="#">
MFG THWBM -
Du musst die Seite nicht neu laden um an den neuen String zu kommen, dafür hast du doch AJAX. Bis jetzt schickst du nur eine Anfrage an den Server, verwirfst aber die Antwort. Wenn du mit PHP den String auf dieser Seite ausgibst (update.php) bekommst du ihn direkt in deinem AJAX-Request zurück…
Ich hab das hier vor einiger Zeit schon einmal erklärt, eventuell hilft dir das weiter: *klick* -
Habe ich das jetzt richtig verstanden? Ich ergänze den Link so:
<a onclick="datenbankupdate('?ID=I1&Sicherheitscode=XYZ')" href="#">//XYZ ist jetzt mal der zufällige String den ich aus der Session geladen habe Datenbank updaten </a>
Dann frage ich in der Update.php ab, ob die Variabel "Sicherheitscode" den gleichen Wert enthält wie den, den ich in der MySQL Datenbank abgespeichert habe. So weit müsste das funktionieren, aber wenn ich für die nächste Aktion dann widerum einen anderen Code benutze, dann muss ich den ja nicht nur in der Datenbank, sondern auch in der Session ändern. Hab ich das so richtig verstanden oder nicht?
MFG THWBM -
Dann mal eine fertige Lösung, aber du solltest sie dir genau ansehen damit du sie auch verstehst.
auth.php:<?php session_start(); function generateRandomString($length) { $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $alphabet .= strtolower($alphabet) . '0123456789'; $string = ''; for($i = 0; $i < $length; $i++) $string .= $alphabet[rand(0, strlen($alphabet))]; return $string; } function generateAuthToken() { $token = generateRandomString(32); $_SESSION['token'] = $token; return $token; } function validateAuthToken($token) { return $_SESSION['token'] == $token; } function hasAuthToken() { return isset($_SESSION['token']); } ?>
update.php:<?php include('auth.php'); header('content-type: text/plain'); $token = $_GET['token']; // im Fehlerfall einen neuen Token erstellen if($token == 'ERR') { echo(generateAuthToken()); exit(); } // gibt es überhaupt einen Auth-Token? if(!hasAuthToken()) die('ERR'); // stimmt der Auth-Token? if(validateAuthToken($token)) { // neuen Auth-Token erstellen $token = generateAuthToken(); echo($token); // mach deine Aktion $id = $_GET['ID']; // hier kannst du die DB updaten exit(); } else { die('ERR'); } ?>
index.php:<?php include('auth.php'); // Auth-Token zum 1. Mal erstellen $token = generateAuthToken(); ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de"> <head> <title>Sicherheit mit AJAX</title> <script type="text/javascript"><!-- var authtoken = '<?php echo($token); ?>'; // hier wird der Auth-Token gespeichert function createXMLHttpRequest() { if(window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari return new XMLHttpRequest(); } else { // code for IE6, IE5 return new ActiveXObject("Microsoft.XMLHTTP"); } } function datenbankupdate(data) { var request = createXMLHttpRequest(); request.open('GET', 'update.php' + data, false); request.send(); // Anfrage senden // speicher den neuen Auth-Token authtoken = request.responseText; if(authtoken == 'ERR') alert('Es trat ein Fehler auf. Session abgelaufen?\nVersuch es erneut!'); } // --></script> </head> <body> <p><a onclick="datenbankupdate('?ID=123&token=' + authtoken)" href="#">Datenbank updaten</a></p> </body> </html>
Funktionsweise habe ich schon erklärt, MySQL braucht es hier nicht -
Wow, danke! Ich glaube damit kann ich was anfangen. Ich habe nur noch ein Problem:
Warum dieses
in der auth.php in Zeile 10? Warum nicht einfach$string .= $alphabet[rand(0, strlen($alphabet))];
? Zumal ich bei der von dir vorgeschlagenen Variante manchmal so ganz spontan diesen Fehler hier bekomme:$string .= mt_rand(0, 62);
. Warum? Mache ich etwas falsch?Notice: Uninitialized string offset: 62 in ...\auth.php on line 10
MFG THWBM -
Wie du diesen String erstellst ist völlig egal… er sollte nur irgendwelche unsinnigen Zahlen und Buchstaben haben.
thwbm schrieb:
Hat den Grund, dass mt_rand nur Zahlen zurückgibt und mit dieser Variante alle in
Warum dieses
in der auth.php in Zeile 10? Warum nicht einfach$string .= $alphabet[rand(0, strlen($alphabet))];
?$string .= mt_rand(0, 62);
enthaltenen Zeichen für den String verwendet werden.$alphabet
Zum "Fehler": das ist, weil ich unglücklicherweise auf meinem Server Fehlermeldungen deaktiviert habe, und deshalb ist mir das nicht aufgefallen. Um das zu beheben musst du einfach ein
anhängen, also so:-1
$string .= $alphabet[rand(0, strlen($alphabet) - 1)];
Du machst also nichts falsch
Beitrag zuletzt geändert: 3.10.2012 20:18:08 von hackyourlife -
hackyourlife schrieb:
Hat den Grund, dass mt_rand nur Zahlen zurückgibt und mit dieser Variante alle in
enthaltenen Zeichen für den String verwendet werden.$alphabet
Aber strlen gibt doch nur die Anzahl der Zeichen zurück, also A-Z + a-z + 1-9 = 62. Deshalb habe ich auch die Zahlen 0 und 62 bei dem Zufallsgenerator verwendet. Stimmt mein Gedankengang oder liege ich da falsch?
MFG THWBM -
thwbm schrieb:
Mit diesem Gedankengang bist du so lange richtig, wie folgender Code dabei herauskommt:
hackyourlife schrieb:
Hat den Grund, dass mt_rand nur Zahlen zurückgibt und mit dieser Variante alle in
enthaltenen Zeichen für den String verwendet werden.$alphabet
Aber strlen gibt doch nur die Anzahl der Zeichen zurück, also A-Z + a-z + 1-9 = 62. Deshalb habe ich auch die Zahlen 0 und 62 bei dem Zufallsgenerator verwendet. Stimmt mein Gedankengang oder liege ich da falsch?$string .= $alphabet[rand(0, 62 - 1)];
Und das enspricht$string .= $alphabet[rand(0, 61)];
Grund:
(oderrand()
) erwartet als Parameter den kleinsten und den größten Wert. Da ein Array-Index aber die Werte 0 - (Arraylänge - 1) haben kann braucht es das -1, damt_rand()
die Anzahl der Elemente zurückgibt.strlen()
-
hackyourlife schrieb:
Grund:
(oderrand()
) erwartet als Parameter den kleinsten und den größten Wert.mt_rand()
Ja, deswegen frage ich mich ja, warum ich mir einen String nehmen soll, der immer 62 Zeichen hat, dann die länge ermittele und von der Länge dann 1 Abziehen soll und ich nicht einfach die Zahl 61 nehmen kann. Bedeutet diese Zeile:
$string .= $alphabet[rand(0, 62 - 1)];
jetzt etwa, dass er sich ein zufälliges Zeichen aus dem String $alphabet nehmen soll, wobei das Zeichen das erste Zeichen, aber auch das 62ste oder auch eins dazwischen sein kann? Wenn ja, hatte ich bisher einen Verständnisfehler.
MFG THWBM -
thwbm schrieb:
Ja, genau das bedeutet es
Bedeutet diese Zeile:
$string .= $alphabet[rand(0, 62 - 1)];
jetzt etwa, dass er sich ein zufälliges Zeichen aus dem String $alphabet nehmen soll, wobei das Zeichen das erste Zeichen, aber auch das 62ste oder auch eins dazwischen sein kann? -
hi,
auch mit session und authcode bist du nicht auf der sicheren seite... schließlich sind beide vor einer aktion im browser verfügbar - es ist also kein problem den sessioncode und auch fortlaufend zurückgegebene authcodes für automatisierte anfragen zu benutzen/missbrauchen.
ich denke die einzig wahre sache wäre wirklich bei jedem aufruf serverseitig zu prüfen ob die aktion auch erlaubt ist (z.b. beim kaufen: ist der gegenstand verfügbar ? hat der user genug geld ? etc...) -
hcms schrieb:
Hast du das Szenario verstanden? Es geht darum, dass der User eingeloggt ist und auf eine dritte Seite surft, die versucht die DB (des "Spieles") zu manipulieren…
auch mit session und authcode bist du nicht auf der sicheren seite... schließlich sind beide vor einer aktion im browser verfügbar - es ist also kein problem den sessioncode und auch fortlaufend zurückgegebene authcodes für automatisierte anfragen zu benutzen/missbrauchen.
Und wie kommt durch den Auth-Code die Sicherheit?
Ohne Auth-Code: Der Benuzter loggt sich ein und bekommt eine Session. Wenn er nun eingeloggt ist und auf einer anderen Seite einem Link folgt der irgend eine Aktion in der DB ausführt wird diese Aktion auch ausgeführt, da der User ja auf der echten Seite eingeloggt ist.
Mit Auth-Code: Der Benutzer ist eingeloggt. Die "böse" Seite will ihn ebenfalls dazu bringen eine Aktion in der DB auszuführen, was aber nicht möglich ist, da die Angreiferseite den Auth-Code nicht kennt und ihn auch nicht erfragen kann, da sie die Session ebenfalls nicht hat. Warum braucht dann die Seite im 1. Beispiel die Session nicht? Weil der Link alleine reicht (da er immer gleich ist) und der Browser die Session von der echten Seite kennt.
hcms schrieb:
Was aber das hier besprochene Angriffsszenario nicht verhindert. Natürlich muss es dieses System ebenfalls geben, denn sonst kann der User selbst cheaten! Beides schützt aber nicht vor einem Bot, falls du das meinst…
ich denke die einzig wahre sache wäre wirklich bei jedem aufruf serverseitig zu prüfen ob die aktion auch erlaubt ist (z.b. beim kaufen: ist der gegenstand verfügbar ? hat der user genug geld ? etc...) -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage