PHP/MySQL, Array Problem

Bob Byte

Admiral Special
Mitglied seit
21.02.2002
Beiträge
1.698
Renomée
9
Standort
in meiner Wohnung
Mahlzeit !

Ich bringe mir derzeit PHP/MySQL bei und habe mir dafür ein ziemlich gutes Buch besorgt.
Arbeite die Beispiele im Buch nach bzw mit, leider tritt dabei dieser Fehler auf

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /intranet/www/htdocs//jokes.php on line 48

Dazu mal den Code-Schnipsel:

//Witz Text aus der Datenbank holen
$joke = mysql_query("SELECT JokeText From Jokes "."Where ID=$id");
$joke = mysql_fetch_array($joke);
$joketext = $joke["JokeText"];

Vielleicht habt ihr ja eine Idee, denn so verliert man die Lust am lernen,wenn man absolut nicht weiterkommt :(

Gruß BB
 
Also der Fehler sagt einfach aus, das in $Joke kein resultset deiner Query steht.
Und ich vermute stark, daß das von deinem doppelten $Joke kommt. ersetze mal das erste durch $joke_rs (dann auch bei fetch_array).

edit:

Ich glaube aber dein Hauptfehler kommt von dem komischen "." in deiner Query das muß raus.

MfG

ALT255
 
Hi !

habe beides ausprobiert aber dadurch verschwindet der Fehler nicht.

Ich kann die Funktion zwar benutzen aber es stört trotzdem das eine Fehlermeldung auftaucht.....man soll Fehler ja nicht unterdrücken oder ignorieren ;)

Gruß BB
 
Hi,

versuchs mal so:
PHP:
$query = "SELECT JokeText FROM Jokes WHERE ID = '".$id."'";
$joke = mysql_query($query);
while ($row = mysql_fetch_array($joke))
$joketext = $row["JokeText"];
Wenn das nichts bringt, mach mal ein print $query; und schau dir den Output an.. ev. ist ja $id nicht gesetzt.. (btw: ist ID auch wirklich gross geschrieben ? )
 
ich schreibe ID auch IMMER GROß :)

Reine Übersichtlichkeit :P

ich denke, so wie Skar es geschrieben hat, wird es funzen.
Du hattest bei dir ja auch noch einen
PHP:
"."
zu viel drin ;)

generell sagt die Meldung aus, dass aus deiner DB Anfrage/Query KEIN ARRAY herauszuholen ist.
Das kann passieren, wenn es kein Ergebnis liefert oder wenn die Daten nicht in ein Array passen (das prob mit mysql_fetch_assoc^^).
 
Tach erstmal

du müsstest mit mysql_num_rows überprüfen wie viele Ergebniszeilen zurückgeliefert werden! Wenn es nämlich keine gibt, kann auch mysql_fetch_array keine holen ...

Allerdings kann der Fehler nicht davon kommen, denn in diesem Fall würde mysql_fetch_array false zurückgeben und keinen Fehler hervorrufen. Probier das ganze mal aus mit dem "or die(mysql_error());" ... wenn's da schon einen Fehler gibt dann weißt du was falsch ist.

Kleine Frage: wie hast du error_reporting in der php.ini eingestellt? Stell' das mal auf E_ALL, das ist immer gut so ...

PHP:
settype($id,'integer');
if($id<1)
	die('Fehler: ID kleiner 1');
// else
$joke_query=mysql_query('SELECT JokeText FROM Jokes WHERE ID='.$id)
	or die(mysql_error());
if(mysql_num_rows($joke_query)==1)
{
	$joke = mysql_fetch_array($joke);
	$joketext = $joke["JokeText"];
}
else
	die('Kein Joke mit dieser ID!');
wobei übrigends die ersten 4 Zeilen sehr wichtig sind ... wenn du $id nicht auf Gültigkeit überprüfst hast du eine SQL-Injection programmiert, das ist 'ne Sicherheitslücke ;)

@SKar
um Integerwerte muss man keine Anführungszeichen machen, daran kann's also nicht gelegen haben ...
 
Tach erstmal

Original geschrieben von sniper.de
und wenn man aber die ' drum macht, ist dann die Sicherheitslücke nicht gefixt?

das kommt drauf an was der Benutzer an der Variablen $id rumpfuschen kann ...

Wenn im PHP-Skript abgesichert wird dass in $id nur Integer-Werte (bzw. Ganzzahlen in Form von Strings) stehen dann ist das keine Sicherheitslücke.

Generell würde ich sagen: Wenn du numerische Werte hast, dann mach die Variablen auch zu Integern (oder Floats). Dann hast du auf jeden Fall alle Zeichen, die nicht Zahlen sind, raus und damit auch vom Benutzer eingeschleusten Code der evtl. bösartig ist.

Ich mach das zum Beispiel immer so (z.B. die Seitenangabe bei einem Gästebuch oder ähnliches):
PHP:
// Hole den Seitenparameter
if(isset($_GET['page']))
	$page=$_GET['page'];
elseif(isset($_POST['page']))
	$page=$_POST['page'];
else
	$page=0;

// Sorge für richtigen Typ und gültigen Wert
settype($page,'integer');
if($page<1)
	$page=1;if(isset

die Zeile mit dem settype ist übrigends wichtig, da $_GET['page'] oder $_POST['page'] immer Strings sind ;)

Die Sicherheitslücke ist mit einfachen Apostrophen nicht raus. Das wichtige hier ist, dass $id wirklich nur Nummern enthält (also jedes einzelne zeichen muss im Bereich 0 bis 9 liegen, Leerzeichen sind nicht erlaubt).

Wenn der Benutzer die ID z.B. so manipulieren kann: index.php?ID=3 dann kann er ja im Browser alles mögliche nach dem Fragezeichen angeben ... in diesem Fall wäre das keine wirkliche Sicherheitslücke da nach einem WHERE ja keine interessanten Operationen mehr möglich sind (man kann z.B. keine weitere Query mehr anhängen). Man kann höchstens ungültige Werte angeben und durch die Fehlermeldungen mehr über die Bauart des Scripts erfahren (was theoretisch auch ausgenutzt werden kann ...)

Ein gutes, praktisches Beispiel für einen nicht überprüften Wert, der immer ein Integer sein sollte ;)

Im Informatikraum meiner Schule gab es ein System zur Freischaltung des Internetzugangs, das auf PHP + Shellscripts basierte. Es gab 2 Seiten: Einmal eine mit einem Formular, in dem man eine Zeitdauer eintragen konnte. Dieses Formular wurde dann an ein PHP-Script geschickt, das dann auf dem Server ein Shellscript (ich weiß den Dateinamen nicht mehr, könnte so was wie internet.sh gewesen sein) gestartet hat, und zwar mit der ungeprüften Eingabe als Parameter: system("internet.sh $dauer"); war der verhängnisvolle Befehl. Da die Funktion system durchaus mehrere Befehle auf einmal ausführen kann und $dauer in keinster Weise geprüft wurde, hatte ich praktisch einen Shell-Zugang zu dem Server: eine Eingabe wie "45; uname -a" erzeugte dann ja diesen Aufruf: system("internet.sh 45; uname -a");. Dieser Aufruf startet zuerst das internet.sh-Script und danach das Programm uname, das auf einem Unix/Linux-Rechner so was wie Kernelname, Kernelversion, Architektur etc. ausspuckt. Und die Rückgaben von system wurden außerdem auch noch wieder ausgegeben (wie echo() oder print()). Darüber hab' ich dann unter anderem das Administratorpasswort für alle Rechner rausbekommen, das war nämlich in einem anderen Shell-Script gespeichert, das ich mir mit "45; cat laufwerk_e_loeschen.sh" habe ausgeben lassen :D

Also merke: Alle Variablen auf Gültigkeit prüfen !!! (Das ist das wichtigste).

Bei Strings (wenn ein Benutzer nen Benutzernamen oder Passwort oder seine Hobbys oder so was eingibt) ist das nicht möglich, da ist ja jedes Zeichen erlaubt (ok, theoretisch sind die ersten paar Zeichen des ASCII-Zeichensatzes nicht erlaubt, z.B. das Null-Byte ...). Also muss man dafür sorgen dass die Eingaben in dem String nicht "ausbrechen". Wenn man z.B. etwas in der Datenbank aktualisiert ("UPDATE `benutzer` SET `passwort`='neuespasswort' WHERE `username`='Procyon'") dann muss das neue Passwort ja zwischen den Apostrophen stehen. Wenn jetzt in dem String, den der Benutzer eingegeben hat, aber auch ein Apostroph ist, dann würde dieses eingegebene Apostroph den String vorzeitig beenden und es wird evtl. was in die Query reingeschrieben, was nur in dem String sein sollte ...

Beispiel:

PHP:
// wir haben die *rohen* Benutzereingaben in $username und $passwort
mysql_unbuffered_query
	(	"UPDATE
			`benutzer`
		SET
			`passwort`
			=
			'$passwort'
		WHERE
			`username`
			=
			'$username'"
	) or die(mysql_error());

Wenn man jetzt als Benutzernamen folgendes eingibt: "Procyon' OR `username`='admin" (ohne die doppelten Anführungszeichen) dann sieht die Query nach dem Einsetzen der Variablen $username und $passwort folgendermaßen aus:
Code:
UPDATE
	`benutzer`
SET
	`passwort`
	=
	'neuespasswort'
WHERE
	`username`
	=
	'Procyon' OR `username`='admin'
und siehe da: wir haben den Schutz der Apostrophe umgangen und ein " OR username=admin" eingeschleust, was das Passwort für den Administrator ändern würde ;)

Die einzige Möglichkeit, sich dagegen zu schützen, ist, Apostrophe in der Benutzereingabe zu "verbieten". Aber wenn man die wirklich verbieten würde, dann könnte ich in diesem Text auch keine Apostrophe benutzen ...

Die Lösung ist, alle Apostrophe, die ein Benutzer eingegeben hat, so zu kennzeichnen, dass MySQL erkennt: Dieses Apostroph gehört nicht zu den Steuerzeichen, sondern zu dem eingegebenen String.

Man hat sich darauf geeinigt, allen Steuerzeichen, die keine sein sollen (wie unser Apostroph), einen Backslash voranzustellen. Da ein Backslash damit auch ein Steuerzeichen ist, wir jedem vom Benutzer eingegebenen Backslash ebenfalls ein Backslash vorangestellt, der besagt: Der folgende Backslash gehört zum String und ist kein Steuerzeichen.

Wenn man das dann auf die Benutzereingaben in $username und $passwort anwendet, dann wird aus "Procyon' OR `username`='admin" ein "Procyon\' OR `username`=\'admin" und die Query sieht so aus:
Code:
UPDATE
	`benutzer`
SET
	`passwort`
	=
	'neuespasswort'
WHERE
	`username`
	=
	'Procyon\' OR `username`=\'admin'
In diesem Fall sucht MySQL nach einem Benutzernamen der "Procyon' OR `username`='admin" lautet (ohne die "), da die beiden \' den String nicht beenden. Das Apostroph ist kein Steuerzeichen mehr, wohl aber der Backslash, der von MySQL entfernt wird, da er ja nicht zu der Eingabe gehört, sondern nur signalisiert, dass das nachfolgende Apostroph kein Steuerzeichen ist.

Alles klar? ;)
 
sehr aufschlussreich, alles klar :)

ich denke du solltest irgendwann mal versuchen eine Sicherheitslücke auf meiner Schulpage auszunutzen ;)
Aber nicht bevor ich es dir erlaube ^^
 
@Procyon_theEvil: sehr interessante Ausführungen! Das man Usereingaben immer prüfen sollte, war mir schon klar, nur was man damit auch anstellen könnte war mir nicht bewusst! (jetzt ist mir auch klar warum du Procyon_theEvil heisst ;D )
btw: settype()/gettype() sind ja zwei ganz interessante Befehle - kannte ich noch gar nicht..
 
Tach erstmal

(jetzt ist mir auch klar warum du Procyon_theEvil heisst ;D )

Naja ich find den Zusatz eher dumm aber es gibt hier in diesem Forum schon einen Procyon (der allerdings nur einen einzigen Beitrag geschrieben hat und sonst nix ... und den vor 3 Jahren ;D) ...

Also musste ich mir einen anderen Nick ausdenken

(Sorry for SPAM)
 
Zurück
Oben Unten