Du bist nicht angemeldet.

Lieber Besucher, herzlich willkommen bei: OS X Entwicklerforum. Falls dies dein erster Besuch auf dieser Seite ist, lies bitte die Hilfe durch. Dort wird dir die Bedienung dieser Seite näher erläutert. Darüber hinaus solltest du dich registrieren, um alle Funktionen dieser Seite nutzen zu können. Benutze das Registrierungsformular, um dich zu registrieren oder informiere dich ausführlich über den Registrierungsvorgang. Falls du dich bereits zu einem früheren Zeitpunkt registriert hast, kannst du dich hier anmelden.

  • »Rosi-janni« ist der Autor dieses Themas

Beiträge: 523

Wohnort: Wittlich

Beruf: Schüler

Hobbys: Motorsport, Coden, Twitter: rosijanni

  • Private Nachricht senden

1

Sonntag, 26. Juni 2011, 00:33

PHP- Einen Token aus einem String rausnehmen

Tag auch!

Hier komme ich mal wieder zu einem Problem mit PHP.
Die Geräte müssen sich bei mir in der DB anmelden, also einloggen. Nur während sie eingeloggt sind, sollen sie Pushs erhalten.
Darum sammle ich pro Account DeviceTokens. Wenn sich abgemeldet wird, soll der Token natürlich wieder daraus.
Ich habe in der DB einen 'longText' für DeviceTokens und jeder Token wird separiert mit einem Semikolon (keine Whitespace) dort hinein geschrieben.
Also muss der aktuelle Token aus diesem String auch wieder raus. Da ich wirklich nicht sehr phpbewand bin, scheitere ich an diesem Problem.
Das Einfügen funktioniert, nur das rausnehmen nicht.

Mein Code dazu sieht so aus:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php
error_reporting(E_ALL);

$dbname="q_db0";
$dbhost="localhost";
$dbuser="USER";
$dbpass="PASSWORD";
mysql_connect($dbhost,$dbuser,$dbpass);
mysql_select_db($dbname);

$username = $_GET[username];
$password = $_GET[password];
$getToken = $_GET[token];

$ergebnis = mysql_query("SELECT * FROM accounts WHERE mail='$username'");  
$all = mysql_fetch_array($ergebnis);	

$asdf = $all[pushTokens];

$teile = explode(";", $asdf);

if ($password == $all[passwort]){
	
	$vorhanden = FALSE;
	
	// Gucken ob der Token wirklich in der DB drin ist…
	for($x = 0; $x < count($teile); $x++) {  
		if ($teile[$x] == $getToken){
			$vorhanden = TRUE;
		}
	}
	
	// Wenn er drin ist, dann ein neuen String für DB schreiben, aber OHNE den aktuellen Token
	if ($vorhanden == TRUE){
		
		echo "Vorher: $asdf<br>";
		
		// Alle Token durchlaufen
		for($x = 0; $x < count($teile); $x++) {  
			
			if ($teile[$x] == $getToken){
				
			}else{
				// Nur hinzufügen, wenn der Token nicht der Aktuelle ist
				$doof .= ";$teile[$x]";
			}
			
		}
		
		echo "Nachher: $doof<br>";
		
		// Den Rest nachher OHNE den aktuellen Token in die DB updaten.
		$sql = "UPDATE accounts SET pushTokens = '$doof' WHERE mail='$username'";

		$db_erg = mysql_query($sql) 
   		or die(mysql_error());
		
	}
	
	echo "login,0";
	
}else{
	
	echo "login,1";
		
}

?>


Irgendwie wird bei der 'Vorher'-Ausgabe schon ein Semikolon voran gestellt, was falsch ist.
Bei der 'Nachher'-Ausgabe steht einfach nur das Selbe. Irgendwas mache ich da falsch, aber was?

Beiträge: 799

Wohnort: Berlin

Beruf: Student, Freelancer

  • Private Nachricht senden

2

Sonntag, 26. Juni 2011, 09:19

Mhm, du weißt aber schon das du damit gegen jegliche Regeln der Kapselun und Normalisierung "verstößt", oder? Wenn du nicht mehrere UDID's in einem Feld speicherst, hast du erstens das Layout deiner DB verbessert und zweitens, so zusagen in einem Abwasch, die auch noch die 68 Zeilen unnötigen Code gespart :)

3

Sonntag, 26. Juni 2011, 09:29

Quellcode

1
2
3
4
5
6
<?php
error_reporting(E_ALL);
…
$username = $_GET[username];
$password = $_GET[password];
$getToken = $_GET[token];
Wenn Du wirklich mit E_ALL arbeiten würdest, würde PHP Dir (zurecht) einen Haufen Warnings um die Ohren werfen. Nicht-numerische Keys von Arrays gehören in Anführungszeichen oder Hochkommata.

Quellcode

1
$ergebnis = mysql_query("SELECT * FROM accounts WHERE mail='$username'");
Bitte informier Dich dringend mal über den Begriff »SQL Injection«.

Quellcode

1
2
3
4
5
6
	// Gucken ob der Token wirklich in der DB drin ist…
	for($x = 0; $x < count($teile); $x++) {  
		if ($teile[$x] == $getToken){
			$vorhanden = TRUE;
		}
	}
Ich glaube, Du solltest Dir mal die Funktion in_array() ansehen.

Was Dein sonstiges Vorgehen betrifft: wenn ich den Wunsch hätte, eine Liste von Werten in einem Datenbankfeld aufzubewahren und einfach Werte hinzufügen, entfernen oder auf deren Vorhandensein testen zu wollen und darüber hinaus die Werte nicht in einer SQL-Query verwendet werden müssen, würde ich nichts anderes machen als einfach ein serialisiertes Array in das Feld zu stecken.

Carsten

  • »Rosi-janni« ist der Autor dieses Themas

Beiträge: 523

Wohnort: Wittlich

Beruf: Schüler

Hobbys: Motorsport, Coden, Twitter: rosijanni

  • Private Nachricht senden

4

Sonntag, 26. Juni 2011, 10:13

Okay danke.

Zum Thema Error-Report: Entweder ich habe das Teil nicht ganz verstanden, oder ich hab was vergessen. Denn Warnings gibt er keine.
SQL Injektion ist mir ein Begriff, dabei kann man z.B. ja über die GET URL Code in die DB einschleusen, aber wie sollte ich bei einem NSURL-Request sonst noch Daten übermitteln? GET ist das Einfachste, aber POST geht doch sicher auch, ist das denn 'sicherer'?
Da ich wirklich nur mal eine Woche mir neulich PHP angeschaut habe, weiss ich wirklich nicht genau, was ich alles beachten muss.

Aber warum serialisiert speichern? Ich weiss nicht wirklich, wie ich dynamisch Felder erzeugen lassen soll und wie ich sie wieder lösche, durchlaufe.
Daher war eben meine Annahme, dass es richtiger wäre, alles in ein Feld zu schreiben.

5

Sonntag, 26. Juni 2011, 10:39

SQL Injektion ist mir ein Begriff, dabei kann man z.B. ja über die GET URL Code in die DB einschleusen, aber wie sollte ich bei einem NSURL-Request sonst noch Daten übermitteln? GET ist das Einfachste, aber POST geht doch sicher auch, ist das denn 'sicherer'?
Da ich wirklich nur mal eine Woche mir neulich PHP angeschaut habe, weiss ich wirklich nicht genau, was ich alles beachten muss.

1. Böser Nutzer liesst Kommunikation deiner App mit.
2. Sieht dann Request wie etwa register.php?username=foo
3. Ändert das um in register.php?username='; drop table accounts;--
4. Deine PHP Anwendung führt dann (sofern magic quotes nicht an ist -- ist jetzt zu illustrationszweckene) folgendes aus
SELECT * FROM accounts WHERE mail=''; drop table accounts;--'
5. Schon hat irgendein Typ mit Langerweile Deine ganze DB gelöscht.
C++

6

Sonntag, 26. Juni 2011, 11:44

Zum Thema Error-Report: Entweder ich habe das Teil nicht ganz verstanden, oder ich hab was vergessen. Denn Warnings gibt er keine.
Dann ergänze mal das hier zu Beginn des Skripts:

Quellcode

1
ini_set('display_errors', 1);


SQL Injektion ist mir ein Begriff, dabei kann man z.B. ja über die GET URL Code in die DB einschleusen, aber wie sollte ich bei einem NSURL-Request sonst noch Daten übermitteln? GET ist das Einfachste, aber POST geht doch sicher auch, ist das denn 'sicherer'?
Die HTTP-Methode ist egal – die Angreifbarkeit liegt nicht in der Methode, sondern in falscher oder fehlender Vorbereitung von Strings, die Du an die Datenbank schickst. BTW geht es nicht nur ums Einschleusen, sondern auch um das Umgehen von Authentifizierung oder das Löschen oder Ändern von Daten.

Da ich wirklich nur mal eine Woche mir neulich PHP angeschaut habe, weiss ich wirklich nicht genau, was ich alles beachten muss.
Dann wäre es m.E. dringendst angeraten, dass Du Dir ein Buch wie »PHP-Sicherheit« von Kunz/Esser durchzulesen, bevor Du Dich als leichte Beute von Script Kiddies exponierst.

Aber warum serialisiert speichern? Ich weiss nicht wirklich, wie ich dynamisch Felder erzeugen lassen soll und wie ich sie wieder lösche, durchlaufe.
Daher war eben meine Annahme, dass es richtiger wäre, alles in ein Feld zu schreiben.
Ob es »richtig« ist, kann ich nicht beurteilen. Von dem, was Du schreibst, würde ich sagen: Dein Vorgehen ist zumindest nicht lehrbuchmäßig, weil Du nicht normalisierst, aber in Zeiten von NoSQL-Datenbanken wirst Du sicher nicht mehr schief deswegen angesehen, wenn die Normalisierung nur Bloat, aber sonst keinen Nutzen mit sich brächte.

Dein Problem lässt sich im Wesentlich, sofern ich’s richtig verstanden habe, auf diese Punkte runterbrechen:
  • Du willst in einem einzelnen Datenbankfeld mehrere Werte aufbewahren
  • Du willst effizient einen Wert hinzufügen können, sofern er noch nicht da ist.
  • Du willst testen können, ob ein Wert enthalten ist.
  • Du willst einen Wert aus der Menge entfernen.

Der naheliegendste Ansatz in PHP bestünde darin, die Werte als Keys eines assoziativen Arrays zu verwenden (Wert zum Key jeweils 1 oder true), mit empty() zu testen, ob ein Key vorhanden ist, einen neuen Key mit Wert 1 hinzuzufügen und einen Key mit unset() zu entfernen. Wenn das Array dann gespeichert werden soll, verwendet man bevorzugt – sofern auf dem Server vorhandne – json_encode() oder serialize(); beim Auslesen natürlich das entsprechende Gegenstück.

Carsten

7

Sonntag, 26. Juni 2011, 11:48

3. Ändert das um in register.php?username='; drop table accounts;--
4. Deine PHP Anwendung führt dann (sofern magic quotes nicht an ist -- ist jetzt zu illustrationszweckene) folgendes aus
SELECT * FROM accounts WHERE mail=''; drop table accounts;--'
5. Schon hat irgendein Typ mit Langerweile Deine ganze DB gelöscht.
Das ist beim konkreten Beispielcode falsch, denn die alte mysql_*-API aus dem Beispiel kann in einem Funktionsaufruf nicht mehrere Queries ausführen.

Was natürlich für andere Datenbanken und andere Treiber nicht gilt.

Carsten

  • »Rosi-janni« ist der Autor dieses Themas

Beiträge: 523

Wohnort: Wittlich

Beruf: Schüler

Hobbys: Motorsport, Coden, Twitter: rosijanni

  • Private Nachricht senden

8

Sonntag, 26. Juni 2011, 15:23

Ehm die Mail und Passwort sind mit 16000bit RSA verschluesselt in der DB. Also wer da einen SQL Code in den kodierten RSA String bekommt ist schon gut.
Ich guck nachher zu Hause mal nach Euren detailierten Antworten. Sitze beim 24h Rennen ;-)

9

Sonntag, 26. Juni 2011, 15:37

Ehm die Mail und Passwort sind mit 16000bit RSA verschluesselt in der DB. Also wer da einen SQL Code in den kodierten RSA String bekommt ist schon gut.
Ich werde mal ganz deutlich: es ist scheißegal, was für kryptographische Geschütze Du auffährst, solange sie sich umgehen lassen.

Nehmen wir als Beispiel an, Du verwendest folgende Query, um einen Benutzer zu authentifizieren:

Quellcode

1
SELECT userid FROM usertable WHERE password = 'gehashtes-Passwort-mit-Salt' AND username = 'name-des-users';
Gehen wir außerdem davon aus, dass der »username« nicht gequoted aus einer Request-Variable übernommen wird und ein Angreifer als Wert der Variablen »' OR '1'='1« übergeben hat (natürlich URL-encoded). Dann ergibt sich diese resultierende Query:

Quellcode

1
SELECT userid FROM usertable WHERE password = 'gehashtes-Passwort-mit-Salt' AND username = '' OR '1'='1';
. Dämmert Dir was? Es werden alle Benutzer geholt, weil der WHERE-Ausdruck immer zu TRUE evaluiert. Ergo wird der Angreifer die User-ID irgendeines Users verpasst bekommen.

Klar – Verschlüsselung erschwert den Angriff, aber nicht, weil Du verschlüsselst, sondern weil beim Verschlüsseln oder Hashen das Hochkomma nicht erhalten bleibt. In dieser Hinsicht hat Verschlüsselung also den Nebeneffekt, die Variable in gewisser Weise auch zu quoten; die Verschlüsselung selbst hat aber keine Relevanz, so dass die Stärke des Algorithmus schlicht nicht interessiert.

Carsten

10

Sonntag, 26. Juni 2011, 16:16

Das ist beim konkreten Beispielcode falsch, denn die alte mysql_*-API aus dem Beispiel kann in einem Funktionsaufruf nicht mehrere Queries ausführen.

Heh, OK. Hatte primär gehofft, dass es so leichter nachvollziehbar ist. Ich mach halt kein PHP.

Mail und Passwort sind mit 16000bit RSA verschluesselt in der DB

W-T-F?
1. Warum asymmetrische Verschlüssellung? Ergibt gar kein Sinn, oder? Läd der Nutzer immer seinen Key mit hoch oder was?
2. 16kbit? 2kB? Ist das nicht ein wenig zu hardcore? Dauert das nicht Ewigkeiten in der Benutzung?
C++

  • »Rosi-janni« ist der Autor dieses Themas

Beiträge: 523

Wohnort: Wittlich

Beruf: Schüler

Hobbys: Motorsport, Coden, Twitter: rosijanni

  • Private Nachricht senden

11

Sonntag, 26. Juni 2011, 16:57

Es geht nur um eine userfreundliche eindeutige Authentifizierung. Ich brauche den private Key nicht. Ich möchte damit nur zum
Ausdruck bringen, dass es rein Mathematisch ja nicht mal mehr möglich ist, dass ein 'Einbrecher' in die DB oder ich an die Userdaten komme. Die DeviceToken habe ich momentan da noch nicht bei. Mal sehen, wie sich das in der Benutzung zeigt beim DeviceToken. Natürlich dauert es beim
erstmaligen Login ein wenig, aber es geht um Sicherheit und das möchte ich auch zeigen.

Zur Injection: Wenn die Ubergabe von Daten so dramatisch unsicher ist, muss es ja einen anderen, sichereren, Weg geben. Sonst würde es ja niemand nutzen. Da ich aber nur einen Shell/PHP/SQL Server habe, muss ich aber mit PHP wohl arbeiten. Denn eine SSH Verbindung und das Eintragen via Shell ist dann ja genauso unsicher.
Aber ich hab noch nicht alle Antworten gelesen, bis heute Abend aber natürlich.

12

Sonntag, 26. Juni 2011, 17:06

Es geht nur um eine userfreundliche eindeutige Authentifizierung. Ich brauche den private Key nicht. Ich möchte damit nur zum
Ausdruck bringen, dass es rein Mathematisch ja nicht mal mehr möglich ist, dass ein 'Einbrecher' in die DB oder ich an die Userdaten komme. Die DeviceToken habe ich momentan da noch nicht bei. Mal sehen, wie sich das in der Benutzung zeigt beim DeviceToken. Natürlich dauert es beim
erstmaligen Login ein wenig, aber es geht um Sicherheit und das möchte ich auch zeigen.

Ich versteh immer noch nicht ganz, wovon Du redest. Du brauchst den Private-Key nicht? Warum dann überhaupt RSA?

Davon abgesehen: Gut, dass Du allen Nutzern vertraust. Und wenn ein legitimer Nutzer dann doch anfängt, an den Parametern zu spielen?

Zur Injection: Wenn die Ubergabe von Daten so dramatisch unsicher ist, muss es ja einen anderen, sichereren, Weg geben. Sonst würde es ja niemand nutzen. Da ich aber nur einen Shell/PHP/SQL Server habe, muss ich aber mit PHP wohl arbeiten. Denn eine SSH Verbindung und das Eintragen via Shell ist dann ja genauso unsicher.
Aber ich hab noch nicht alle Antworten gelesen, bis heute Abend aber natürlich.

Ja. Prepared Statements etwa. Verwendet jeder, auch in PHP, der auch nur etwas Sicherheitsbewusstsein hat.
Ausserdem hindert Dich niemand daran, $username etc. auf Sinn zu prüfen. Etwa um prinzipiell auszuschliessen, dass es Zeichen wie '`%- enthält, die potentiell ärger machen.
C++

  • »Rosi-janni« ist der Autor dieses Themas

Beiträge: 523

Wohnort: Wittlich

Beruf: Schüler

Hobbys: Motorsport, Coden, Twitter: rosijanni

  • Private Nachricht senden

13

Sonntag, 26. Juni 2011, 17:42

Okay das heisst ich muss von Hand hingehen, um sicher zu stellen, dass es kein PHP/SQL Kommando ist?
Das Problem ist wohl also, dass ich eine öffentliche URL habe?
Wenn ich diese URL schützen würde in irgendeiner Weise (wie weiss ich grade nicht), kann ich doch trotzdem wie geplant verfahren? Also ich könnte dann meinen kodierten Stuff schreiben/lesen lassen?

Es geht bei meiner Anwendung von RSA in dem Fall nur um Sicherheit. Ich mache das doch nur, damit sich ein User identifizieren kann, aber auch nichts zwei Mal in der DB steht an Nutzernamen. Ich kodiere alles, aber ich will es gar nicht wieder dekodieren. Also brauche ich den Privatekey nicht und kann somit sagen, dass die Daten nicht entschlüsselbar sind. (Also natürlich könnte man, wenn man mehrere 100 Jahre Langeweile hat).

Ich brauche zwingend diese User/Account/Server-Schnittstelle. Primär wegen den Push-Benachrichtigungen momentan, später wird dort vielleicht das Ein oder Andere ergänzt.

Also Fazit: Ich muss von Hand gucken, dass kein SQL/PHP Kommando mitkommt?

14

Sonntag, 26. Juni 2011, 18:34

Okay das heisst ich muss von Hand hingehen, um sicher zu stellen, dass es kein PHP/SQL Kommando ist?
Das Problem ist wohl also, dass ich eine öffentliche URL habe?
Wenn ich diese URL schützen würde in irgendeiner Weise (wie weiss ich grade nicht), kann ich doch trotzdem wie geplant verfahren? Also ich könnte dann meinen kodierten Stuff schreiben/lesen lassen?

Es ist generell ratsame, den Nutzer erstmal als "böse" anzusehen und alle eingaben genauer zu prüfen. Das hat wenig bis gar nichts mit der URL zu tun, sondern schlicht und ergreifend damit, dass Daten die von einem potentiell bösem Nutzer kommen ungeprüft in ein SQL-Statement eingefügt werden. Ein böser Nutzer hat volle Kontrolle über diese Daten und dadurch
im schlimmsten Fall volle Kontrolle über das SQL-Statement.
Prüfsummen über der URL (wie Typo3 das macht/gemacht hat?) halte ich für hirnverbrannt oder zumindest für wenig hilfreich. Wenn Deine App die URL verschlüsselt, kann der Nutzer schauen, wie die App das macht und es einfach Nachahmen. Oder er trägt in Deiner App schon gleich ins "Nutzername"-Feld etwas ein, woran sich Dein Server verschluckt.

Es geht bei meiner Anwendung von RSA in dem Fall nur um Sicherheit. Ich mache das doch nur, damit sich ein User identifizieren kann, aber auch nichts zwei Mal in der DB steht an Nutzernamen. Ich kodiere alles, aber ich will es gar nicht wieder dekodieren. Also brauche ich den Privatekey nicht und kann somit sagen, dass die Daten nicht entschlüsselbar sind. (Also natürlich könnte man, wenn man mehrere 100 Jahre Langeweile hat).

RSA ist aber komplett der falsche Ansatz. Dafür nimmt man Hashfunktionen. Am besten gesalzen und tausend mal hintereinander ausgeführt. So kann man genauso ausschliessen,
dass jemand das dekodiert. Quasi:
hash = sha1( "lalala" + sha1( "bobo" + sha1("fufu" + sha1( USERNAME + "fff") ) ) )
liess Dich da vielleicht mal ein, wenn Du soviel Wert auf Sicherheit legst.

Also Fazit: Ich muss von Hand gucken, dass kein SQL/PHP Kommando mitkommt?

Von Hand gucken, ob die Nutzereingabe sinnvoll ist.
Um sich vor SQL-Injections zu schützen hatte ich Dir schon prepared statements genannt. Schnell gegoogelt ergibt das sogar ein "TuT" für PHP:
http://www.tutorials.de/php-tutorials/27…statements.html
Aber keine Garantie, ob da nicht auch Quatsch drin steht, aber zumindest schonmal ein Ansatzpunkt ;)
C++

Beiträge: 6 391

Wohnort: Herdecke

Beruf: Selbständig

Hobbys: Gleitschirmfliegen, RC-Helikopter, Faustball

  • Private Nachricht senden

15

Sonntag, 26. Juni 2011, 18:46

Prepared Statements haben auch noch den tollen Vorteil, dass man jedweges Sonderzeichen im Usernamen benutzen kann. Also ich könnte mich auch Claus"DerTolleHeld"'zu'Bönnhoff nennen und das SQL Statement würde noch funktionieren.

Gruß

Claus
Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)

16

Sonntag, 26. Juni 2011, 21:30

Um sich vor SQL-Injections zu schützen hatte ich Dir schon prepared statements genannt.
Ist natürlich prinzipiell korrekt, aber im Fall der in der Frage verwendeten Anno-Dazumal-MySQL-API nicht zielführend, weil die nicht weiß, was Prepared Statements sind.
Aber man kann natürlich auch selbst explizit quoten – bei der alten API mit mysql_real_escape_string(), bei z.B. PDO mit PDO::quote().

Carsten

Social Bookmarks