Ein paar grundlegende Sachen zur Sicherheit in PHP / MySQLi

Wenn ein angemeldeter Benutzer eine SessionID präsentiert, welche durch einen Login durch IP a.b.c.d erstellt wurde, dann kann er nicht plötzlich von der IP w.x.y.z kommen. Das bedeutet im Regelfall, dass die SessionID geklaut wurde. In dem Falle einfach die präsentierte SessionID löschen (session_destroy() oder noch besser: Eine spezielle Fehlermeldung drin speichern und diese dem legitimen Benutzer zeigen. Gleichzeitig natürlich dafür sorgen, dass dieser nicht mehr eingeloggt ist)
Hierbei aber nicht die komplette IP speichern (Datenschutz) und vergleichen, sondern nur die ersten 2 oder 3 Blöcke. Idealerweise sollte die Einstellung vom Benutzer festgelegt und auch komplett abschaltbar sein.
Aus Datenschutz hätte ich die IP gehashed und dann verglichen. Dann kann ich diese nicht direkt einsehen, aber prüfen ob es sich um die selbe IP handelt :)

Okay ich dann kille ich also die Session. Sollte ich auch die Session des echten Users killen? Wenn ja wie komme ich an diese ran?

Macht es dann mehr Sinn den Account zu sperren und zusätzlich zu prüfen bei jedem Aufruf ob ein Account gesperrt ist? Wobei das ja eine hohe Datenlast für die Datenbank hat, oder? Dann müsste ich nicht zwangsläufig auf die Session zugreifen.

Wenn du den aber auch als Passwort zum ver- und entschlüsseln verwenden willst, dann wirds etwas tricky. Dann sollte dieser nicht so einfach in der Datenbank am Benutzer hängen sondern extra geschützt wo anders. Ausserdem sollte sich der Salt normalerweise bei jeder Passwortänderung auch ändern. Wenn du hiermit dann irgenwas verschlüsselt hast, dann kommst du da nicht mehr dran.
Extra Tabelle für die Verschlüsselungs-Schlüssel, mit extra Benutzer und Passwort. Am besten auch von einem extra Service aus anzusprechen, nicht von deiner Hauptanwendung aus drauf gehen. Der Extraservice bietet eine definierte Schnittstelle an und gibt die Schlüssel niemals raus. Zum Beispiel nur per REST ansprechbar, "verschlüssele Datei X für den Benutzer Y" oder "entschlüssele Datei Z für Benutzer X und liefere Daten".
Das war nur eine Idee, dass ich den dazu nutzen könnte, um den Aufwand gering zu halten.

Reicht denn für das verschlüsseln und entschlüsseln theoretisch auch ein Passwort das ich für alle User nutze? Oder wäre es zu unsicher?

Ich möchte meinen Usern eine Funktion zur Verfügung stellen, wo sie nur bestimmten anderen Usern Ihre Email und ggfs. Handynummer freigeben. Diese sollen halt aber wirklich nur von diesem User lesbar sein und eben nicht als Klartext in der Datenbank stehen. Wobei das mit Email vielleicht übertrieben ist, weil die Email im Feld Email ja als Klartext steht. :rolleyes:

Natürlich kannst du den Salt auch per SQL erzeugen lassen, dieser muss nicht kryptografisch sicher sein.
Wie mache ich das denn? Gibt es eine spezielle Funktion dafür?
 
Aus Datenschutz hätte ich die IP gehashed und dann verglichen. Dann kann ich diese nicht direkt einsehen, aber prüfen ob es sich um die selbe IP handelt :)
Auch gut, nur halt dann das mit den Blöcken auch bedenken.

Okay ich dann kille ich also die Session. Sollte ich auch die Session des echten Users killen? Wenn ja wie komme ich an diese ran?
Du hast ja die SessionID in der Hand :) Du hast eine Session welche von zwei Benutzern verwendet wird. Lösche die UserID raus (so dass diese nicht mehr eingeloggt sind) und schreib in die Session eine Fehlermeldung.

Macht es dann mehr Sinn den Account zu sperren und zusätzlich zu prüfen bei jedem Aufruf ob ein Account gesperrt ist? Wobei das ja eine hohe Datenlast für die Datenbank hat, oder? Dann müsste ich nicht zwangsläufig auf die Session zugreifen.
Sperren würde ich den nicht gleich. Kann ja auch "aus Versehen" passieren (Mobilnetz, andere IP bei fast allen Anfragen)
Zur Sperre an sich: Prüfe das beim Login. Falls du wirklich Benutzer sperren willst und das ab sofort, so kannst du die Daten zum Beispiel auch irgendwo cachen (memcache, redis). Beim Login immer die Datenbank abfragen, bei jedem Request den Cache befragen. Natürlich musst du dann, wenn du einen Benutzer sperrst, auch den "alten" Eintrag auf dem Cache löschen. Dein Script prüft den Cache, falls der Eintrag nicht da ist, wird er direkt von der DB gelesen und im Cache hinterlegt.

Reicht denn für das verschlüsseln und entschlüsseln theoretisch auch ein Passwort das ich für alle User nutze? Oder wäre es zu unsicher?
Theoretisch, ja. Kommt halt ganz drauf an, was du bauen willst. Ich denke Owncloud / Nextcloud macht das so, also ein Schlüssel für alle.

Ich möchte meinen Usern eine Funktion zur Verfügung stellen, wo sie nur bestimmten anderen Usern Ihre Email und ggfs. Handynummer freigeben. Diese sollen halt aber wirklich nur von diesem User lesbar sein und eben nicht als Klartext in der Datenbank stehen. Wobei das mit Email vielleicht übertrieben ist, weil die Email im Feld Email ja als Klartext steht. :rolleyes:
Dazu musst du die Daten nicht ver- und entschlüsseln, eine Zugriffsprüfung im Select müsste da ausreichen.

Wie mache ich das denn? Gibt es eine spezielle Funktion dafür?
Habe auf die schnelle das hier gefunden, wobei das ziemlich hässlich ist: https://stackoverflow.com/a/13187845/1164913

Wenn du bcrypt() verwendest, dann wird der Salt mit dem Passwort zusammen in einer Spalte gespeichert: https://stackoverflow.com/a/6833165/1164913
Hier musst du dann gar nichts selber machen, die bcrypt-Implementierung generiert einen sicheren Salt.

Grüsse,
BK
 
Du hast ja die SessionID in der Hand :) Du hast eine Session welche von zwei Benutzern verwendet wird. Lösche die UserID raus (so dass diese nicht mehr eingeloggt sind) und schreib in die Session eine Fehlermeldung.
Okay irgendwie steh ich da auf dem Schlauch und kann Dir nicht folgen. :rolleyes: (in der technischen Umsetzung)

Ich habe die Session gestartet und eine Session ID generiert. Wenn der User nun richtige Daten eingibt, dann erzeuge ich also ein Array mit dem gehasheten Username.

In allen relevanten Seiten überprüfe ich dann mit isset $_SESSION["user"] = false und ob die IP der IP des Users gleicht. Wenn das nicht der Fall ist sag ich dann $_SESSION["user"] = false; und übergebe ein Fehlermeldung Session Array nennen wir es z. B. Fehler. Wenn Fehler gesetzt ist gebe ich diesen aus?

Dazu musst du die Daten nicht ver- und entschlüsseln, eine Zugriffsprüfung im Select müsste da ausreichen.
Aber wenn einer die Datenbank hat, hat er die sensiblen Daten direkt weil Sie Dort klar stehen? Wenn das verschlüsselt wäre, ist es doch sicherer, zumindest glaube ich das in meinem Kopf :D
 
Aber wenn einer die Datenbank hat, hat er die sensiblen Daten direkt weil Sie Dort klar stehen? Wenn das verschlüsselt wäre, ist es doch sicherer, zumindest glaube ich das in meinem Kopf :D
Also wenn jemand die Datenbank in der Hand hat, dann hast du ganz andere Probleme :) Ausserdem hätte er dann ja auch dein Verschlüsselungspasswort und könnte von daher auch die Daten ganz leicht entschlüsseln.
Ich denke wir vermischen hier jetzt einiges, du solltest dir mal das ganze Konzept nochmal überlegen und fix definieren. So rein abstrakt geht es halt nur bis zu einem gewissen Level.

Ich habs weiter oben schonmal erwähnt, eine Trennung der Services und der Kommunikation zwischen diesen über eine abstrahierte Schnittstelle dürfte dir am meisten bringen.
Hier dürfte das Stichwort "Microservices" auch angebracht sein. Viele kleine, von einander unabhängige Teile, welche im Ganzen deine Softwarelösung bilden.Jeder Service hat eigene Zugangsdaten zur DB und darf nur das sehen und schreiben, was du definierst.

Okay irgendwie steh ich da auf dem Schlauch und kann Dir nicht folgen. :rolleyes: (in der technischen Umsetzung)
Ich habe die Session gestartet und eine Session ID generiert. Wenn der User nun richtige Daten eingibt, dann erzeuge ich also ein Array mit dem gehasheten Username.
In allen relevanten Seiten überprüfe ich dann mit isset $_SESSION["user"] = false und ob die IP der IP des Users gleicht. Wenn das nicht der Fall ist sag ich dann $_SESSION["user"] = false; und übergebe ein Fehlermeldung Session Array nennen wir es z. B. Fehler. Wenn Fehler gesetzt ist gebe ich diesen aus?
Wenn der Benutzer angemeldet ist, hast du folgende Daten in der Session:
- UserID -> Direkt, brauchst du für viele Abfragen an die DB
- Username -> Brauchst du den wirklich? Oder holst du ihn dir nicht einfach Ad-Hoc aus einem Cache / der DB?
- IP -> Gehashter Teil der IP, welche die Session gestartet hat (den Login gemacht hat)
- IpSettings -> Welche Teile der IP werden geprüft (z.B. Anzahl der Blöcke; 0 = keine Prüfung, 1 = X.0.0.0, 2 = X.Y.0.0 usw)

Bei jedem Request holst du dir die IpSettings aus der Session und hasht die Request-IP entsprechend den Einstellungen. Passt das nicht aufeinander, so setzt du folgende Werte:
- UserID => null
- Username => null
- SessionError => 'Diese Anfrage kommt von einer anderen Adresse als beim Login. Falls du denkst, dass dies ein Fehler ist, so melde dich bitte bei uns oder schalte die Sicherheitsprüfung in den Einstellungen ab.'
Anschliesend leitest du die Anfrage auf die Loginseite um, sprich, du behandelst die Anfrage wie wenn der Benutzer nicht angemeldet wäre.

Auf der Loginseite prüfst du, ob die SessionError gesetzt ist und gibst die Meldung aus.
Sowohl der "bestohlene" User, als auch der Dieb werden auf die Loginseite umgeleitet und sehen die selbe Meldung (da gleiche Session).

Hoffe das war soweit verständlich.

Grüsse,
BK
 
Ja, jetzt habe ich es verstanden :)
Danke für die Erläuterung und die vielen super Antworten hier im Thread. Hast mir richtig weitergeholfen :);)

Eine letzte Frage habe ich noch, die aber weniger mit Sicherheit zu tun hat. Ich will aber kein extra Thema aufmachen.

Ich möchte meinen Code möglichst einfach zu warten halten. Daher habe ich mir gedacht es wäre durchaus Sinn einige Klassen zu nutzen?

Kennst Du vielleicht ein paar gute Tutorials zu solchen? Ein wenig informiert habe ich mich auch schon. Aber ich bin nicht so sicher. Es wird oft von PDO und von OOP Klassen gesprochen. Was ist denn besser bzw. wann setze ich welche ein?

http://www.peterkropff.de/site/php/oop.htm

Peter Kropff hat beides behandelt. Allerdings OOP mit PHP5 ist das schädlich sich daran zu orientieren?
 
Hi,

mit OOP und PHP bin ich jetzt der falsche Ansprechpartner :)
Ich programmiere schon seit längerem nicht mehr mit PHP, bin inzwischen komplett auf Java umgestiegen. Aber eventuell könnte dir @Yaslaw oder ein anderer PHP-Geek hier weiterhelfen?

Grunsätzlich kann ich dir aber sagen, dass das OOP an sich nur ein Konzept ist. Dieser "Gedanke" ist unabhängig von der Sprache, hier variiert nur die Syntax. Wenn du also OOP an sich verstanden hast, dann kannst du das auch in PHP, oder wie ich in Java, anwenden.
PDO ist nur eine Klasse, welche dir von PHP schon zur Verfügung gestellt wird. Diese abstrahiert den Datenbankzugriff, du bindest dich somit nicht mehr an ein DBMS. Du kannst dann z.B. relativ simpel von MySQL auf Oracle umsteigen, ohne dass du am Code was ändern musst. Lediglich deine Queries musst du unter Umständen anpassen.

Grüsse,
BK
 
Zuletzt bearbeitet:
Leider kann ich auch nicht helfen. Aktiv PHP programmiere ich seit etwa 4 Jahren nicht mehr.
 
Ich arbeite bei meinen MySQLi Codes mit OOP. Darum würde ich das gern weiter nutzen. Ich habe nur den Unterschied zu PDO nicht so ganz verstanden. Zumal die Art und Weise ganz anders ist. Aber ich habe neben dem Tutorial das ich zitiert habe noch ein weiteres zu OOP gefunden in Verbindung mit Klassen etc. Da arbeite ich mich einfach zusätzlich noch durch und versuche mich daran :)

Ich würde gern das Thema Spam Schutz noch aufgreifen. Es ist zwar lästig, aber notwendig. Ein hidden Field mitübergeben und prüfen ob leer ist natürlich nicht genug. Auf Captcha würde ich gern verzichten, weil ich selbst es einfach nur lästig finde, ständig kann man selbst die Zeichen nicht erkennen. Kann ich stattdessen einfach Zufallswerte generieren und im Klartext schreiben und dann prüfen ob die stimmen? Oder können die Bots das mittlerweile wenn es keine Bilder sind?

Macht es sonst vielleicht Sinn die Zeit des letzten Request in die Session zu schreiben und zu prüfen ob in den letzten 30 Sekunden was gemacht wurde? Wenn ja, Fehler.
 
Zuletzt bearbeitet:
Mysqli/PDO: Genau, die API (welche Funktionen mit welchen Parametern es wofür gibt) ist anders. ... Mysqli ist außerdem ähnlicher zu den alten mysql_ - Funktionen, was beim Umstieg helfen kann; PDO dagegen funktioniert auch mit ein paar anderen DB-Systemen. (heißt aber nicht, dass man einfach so schnell mal wechseln kann ... zB. die möglichen SQL-Anweisungen sind auch nicht ganz gleich).

Zu dem, was Bots können: Wenn dein System a) ausreichend anders ist als bestehende Systeme, und b) nicht wichtig/verbreitet genug dass ein Mensch sich ransetzt und seinen Bot entsprechend erweitert, gibts eine gute Chance, dass das schon reicht. Bots sind nicht intelligent.
...nur, "ausreichend anders" ist inzwischen gar nicht mehr so einfach. Es wurde schon so viel versucht.

Deine Zufallswerte, die sollen die dann wieder geprüft werden? Wenn sie Teil von den Formulardaten sind wirds nicht helfen, egal ob hidden oder nicht.
 
Ja ich dachte eben ich schreibe die Zufallszeichen in das Formular neben oder über das Input Feld. Der Besucher muss es ja auch irgendwie sehen können. Das scheint also nicht so gut zu sein?

Was für Alternativen gibt es? Wie gesagt Captcha mag ich überhaupt nicht und würde es gern vermeiden :-]
 

Neue Beiträge

Zurück