Beiträge von cottton

    Ich fang jetzt nicht wieder mit Prepared Statements an. Das solltest Du nun kennen.

    Aber

    SELECT * FROM login WHERE username = '$username'

    Wenn im Moment, und|order in der Zukunft ein Fehler passiert,

    bei dem der username nicht (mehr) unique ist,

    dann kannst Du mehr als 1 user laden.


    Der code lässt dann auch noch zu, dass Du solange läufst, bis Du den letzten möglichen user eingeloggt hast.


    Pseudo code Vorschlag:

    Code
    result = SELECT * FROM login WHERE username = :username LIMIT 1;
    if result.count === 1
        row = result->fetchOne() // you expect one, so get one
        if password_verify(password, row.password)
            // all fine
    elseif result.count > 1 
        // error handling for unexpected multiple users on same username
    else
        // error handling for user not found by username

    Vieleicht sollte man solche Antworten das ( ich falsch liege ) dann anders Formulieren weil falsch scheint meine ausage ja nicht gewesen zu sein.

    Ja, ich geb zu, dass war wohl too much von mir, sorry.

    Werde das in dem Post editieren|markieren|notieren.


    timtim

    Ich hätte mir gedacht dass ich Password_verify() im Anschluss an die SQL-Abfrage (hier Zeile-Nr. 20) schreiben müsste?

    "WHERE username=? AND password=?"

    ^ das funktioniert nicht.


    Du bekommst das plain text (klartext) Passwort.

    Du hast (in Deiner db) den hash des Passwortes.


    Du lädsts den User bei username oder email.

    Findest Du ihn, dann nimmst Du den zugehörigen hash und nutzt password_verify() um den das Passwort mit dem Hash zu vergleichen.

    Du bekommst:

    username

    password


    Du suchst in der db nach dem username, der unique sein muss. Oder Du geht nach email, die ja auch unique sein muss.

    Findest Du den User, dann prüfst Du das password gegeb den hash.


    Pseudo code:

    Code
    user = "select ... from user where username = :username"
    if ! user
        // fehler - user nicht gefunden
    else
        // check input vs db users hashed pw
        if password_verify(password, user.hash)
            // ok
        else
            // nicht ok

    Siehe auch Loginpasswort richtig hashen - password_hash() & password_verify()

    Dein Beispiel ist mir schon bewusst, aber da sind wieder injections möglich, weil das Prepare das nicht abfängt, deswegen brauchst du ja eine WhiteList und da kommt das escapen bzw. anderweitige validieren ins spiel, was du ja machst. *_real_escape_string wäre zum prüfen ebenfalls möglicht.

    Könnte man sich streiten. Ich vertraue da einer whitelist (viel) mehr.



    mein "da draußen" läuft über skalierbare Cloud Anwendungen,

    Naja, das wird dann wohl doch ein bissl zu viel für das Forum, oder?



    Hier nur der bezug auf deine Aussage Sicherheit > "speed" Ja, aber nicht über PreparedStatements weil der Sicherheitsaspekt zu den Prepared Statements kleiner ist als der Overhead.

    Für Anfänger, die hier im Forum lernen? Ach komm.

    Das bischen overhead ... wenn überhaupt.

    Ich hab das nicht getestet, aber die real escape Funktion nutzt das db handle,

    also wird die auch beim MySQL Server anfragen. Und das per call (value).


    Prepared Statements schützen nur vor first-order injections nicht second-order

    Bin mir fast ganz sicher, das die Aussage falsch ist.

    Wenn ich second order injection richtig verstanden habe, dann kann*1 beim Verwenden der Daten aus der db

    in einer Query die (2nd order) injection möglich werden.

    *1 Aber dann eben nicht. Denn da nutzten wir doch auch wieder Prepared Statements. :)

    Wer sowas schreibt ... WHERE foo = {$row['foo']} ... gehört ... belehrt :D


    Und genau das is es btw, was ich immer sage: alle Daten sind böse. Selbst die in der eigenen db.

    Und daher gilt doch, was ich am Anfang sagte: die Daten gehen so wie sie sind in die db.


    Lange muss nicht richtig heißen. Pauschal aussagen bekommen pauschal antworten.

    Hehe, you got me there. Naja, Wochen, Momante. Weiß nicht mehr.

    Aber ich bin keiner, der code kopiert. Ich will (so weit möglich) wissen wie und warum etwas funktioniert.

    Gehört auch bissl Glück dazu. Einfach weiter machen!

    Achtung - Lebensgeschichte :D

    Ich komme vom Bau. Hatte dort keine Zukunft. Langzeitarbeitsloser ... .

    Wollte was anderes machen.

    Hab PHP als Hobby gelernt (kein OOP).

    Hab mich beworben. Keine Antwort.

    Auf Nachfrage: OOP muss schon sein.

    OOP gelernt.

    Immernoch kein Glück.

    Jeden "Mist" mitgemacht:

    - Fernstudium bei ILS (ich halte mich mit meiner Meinung dazu mal zurück)

    - Selbststudium

    - ...

    - bis hin zum Bewerbungstraining, wo die mich gefragt haben, was ich eigtl. hier will :D

    aber die waren echt nett =)


    Aber jetzt kommts: beim Bewerbungstraining sollten wir uns um ein Praktikum bemühen.

    Und ich hab Nummern angrufen, bei denen ich dachte "da kommt eh nichts".

    Und BAM - Rückruf von meinem (seit 2016) neuen Boss =)


    Und ab dann, wenn Du drin bist, lernst Du erst richtig.


    Also einfach alles mitnehmen. Auch wenn es total sinnlos scheint.

    Man weiß nie ... :)

    wie ich z.B. vom User sowohl die abzufragenden Felder + dynamische wehre Bedingungen in ein Prepaid Statement setze

    Perfekt: hab das gerade geposted.

    Prepared Statements (PDO) copy|paste Beispiele.



    der overhead von PreparedStatements zu Verschwendung von Ressourcen führt

    Glaub mit - da draußen ist das das geringste Problem.

    Sicherheit > "speed".


    wirkt auf mich auch eher als wolle man sich nicht mit der sache beschäftigen.

    Ich hab mich lange damit beschäftigt.

    Das Problem ist, dass man das nicht so einfach runterschreiben kann.

    Es würde einfach so lang werden, dass es keiner liest.

    Aber es gibt vieles im Netz dazu. Was ich hier versucht ist, den Leuten klar zu machen, dass es "da was gibt" - Problem und Lösung.

    Generell: auf SQL-Injections hinweisen. Alle Daten sind böse.

    Im Detai: in Richtung Prepared Statements lenken, damit die Leute sich damit beschäftigen.


    Und ebenfalls verschleiert es für Anfänger die eigentliche Problematik: die Wichtigkeit der Validierung der eingaben

    Du prüftst, ob die email zu lang ist.

    Du prüfst, ob der nickname legal, nicht leer, und nicht schon vergeben ist.

    ...

    Aber Du kannst nicht alle Injections der Welt kennen.

    Das, also das Thema Sicherheit, überlassen wir dem MySQL Server (bei richtiger Bediehnung).

    Warum Prepared Statements und was ist das: Sichere Programmierung mit PHP


    Beispiele, die (hoffentlich) für sich selbst sprechen,

    und eigtl. auf jedem System laufen sollten:


    Kommentare nur in englisch.

    Naja, ist doch richtig, was er da sagt.

    basically mysqli_real_escape_string will take out the junk in the user input according to a defined character set while prepared statements will make sure if there be any junk in the user input it will be only interpreted as you want it to be interpreted which is usually as user junk :)

    Grob gesagt: mysqli_real_escape_string den "Müll" zu entfernen (maskieren).

    Prepared statements ist der "Müll" egal, da eh nichts passieren kann, da (by default) als string in das Template eingefügt.


    Ich sehe da jetzt kein Gegenargument oder irgandwas, da meine Aussage jetzt als falsch dastehen lässt.

    Sorry, aber das ist viel Quatsch.

    Zu viel, um es hier richtig zu stellen.

    Alleine Das hier:

    Eine escape_string Funktion versucht den kompletten String zu maskieren ( in den meisten Fällen also die komplette Query ),

    Ist schwammig und einfach falsch.


    Siehe einfach meinen Post vorher.

    Oder such im Netz. Stack ist schon eine ganz gute Quelle, da dort sehr viele sofort reagieren würden, wenn jemand Mist erzählt.

    "mysql prepared statements vs mysqli_real_escape_string"

    Zitat

    Should I use mysqli_real_escape_string or should I use prepared statements?

    Prepared statements only. Because nowhere escaping is the same thing. In fact, escaping has absolutely nothing to do with whatever injections, and shouldn't be used for protection.

    While prepared statements offer the 100% security when applicable.


    Also zu Deiner Frage: das erste ist richtig

    Wenn man PreparedStatements verwendet brauch man kein mysqli_real_escape_string

    Nein, auf keinen Fall! Du wirst spätestens dann ein Problem bekommen, wenn ein User sowas eingibt:

    Code
    Mike's eMail

    Durch das einfache Hochkomma zerdrischt du das insert, weil du dann ungefähr sowas in String stehen hast

    Code
    insert into tabelle set abc='Mike's eMail'

    Das scheppert dann. Und SQL-Injektions sind sonst auch möglich.

    Du hast es evtl nicht verstanden.

    Es sollte so aussehen:

    insert into tabelle set abc = :placeholder;


    Die Parameter für Prepared Statements müssen nicht maskiert werden. Der Treiber übernimmt das automatisch. Wenn eine Anwendung ausschließlich Prepared Statements benutzt, kann sich der Entwickler sicher sein, dass keine SQL-Injection auftreten wird. (Wenn aber trotzdem andere Teile der Abfrage aus nicht zuverlässigen Eingaben generiert werden, ist dies immer noch möglich.)

    Versuch doch mal eine SQL-Injection mit Platzhaltern.


    ---

    Wenn jetzt hier mysql values escapen und vars in die Query schreiben (fast schon) empfohlen wird,

    dann werd ich mich wohl verabschieden.

    Seit Jahren versuch ich den "neuen" mitzugeben, damit es irgendwann mal allgemein bekannt sein wird.

    Jetzt gehts auf einmal vollgas rückwärts - als wäre ich heute aufgestanden, und es ist 5 Jahre zurück :D

    Wofür sind den die Punkte und " in deine query String ? Bist du sicher das es so sein muß ?

    Das ist normale PHP string-Verkettung.

    $_POST wird nicht ungefiltert in der Datenbank geschrieben.

    escapen nicht vergessen.

    NEIN., Falsch. (EDIT: ich hab hier übertrieben reagiert. Sorry.)


    romaeVergiss mysqli_real_escape_string. Die Funtion solltest Du nie wieder nutzen. (EDIT: ich hab hier übertrieben reagiert. Sorry.)

    Die Daten werden so wie sie sind in die db geschrieben.

    Außer Passwörter. (Loginpasswort richtig hashen - password_hash() & password_verify())

    Du musst aber alle Daten als nicht vertrauenswürdig ansehen.

    Bei der Ausgabe im (zB) Browser nutzt Du htmlspecialchars() order htmlentities().


    Welche Daten sind nicht verstrauenswürdig?

    Alle!

    Inputeingaben via POST order GET,

    Dateien (csv, txt, json, ... ALLE),

    Eingaben aus der console,

    Daten bezogen von einer API,

    ...,

    und selbst die Daten, die Du in Deiner db stehen hast.


    Zur db: Wer sagt, dass die sicher sind? Hat da nie nie niemals jemand was geändert? 100% sicher? NEIN.


    Also: alle Daten sind böse.

    Und deswegen macht es erst gar keinen Sinn da was beim Speichern in der db zu "escapen" ect.

    ABER: nutze Prepared Statements (PDO oder mysqli. Ich empfehle PDO).

    https://www.php.net/manual/de/pdo.prepared-statements.php

    https://www.php.net/manual/de/…t.prepared-statements.php


    Ich halte so Namen wie $na, $us, etc. für grausam.

    Yap. Sowas geht gar nicht.

    Arne Drews

    Du hast schon recht. Das db Design und|oder die Query sollte das schon klären.

    Aber es kann vorkommen, das mal was geändert wird, und es bei Design nicht mehr geregelt wird.

    ZB "ausversehen" den unique index ge-dropped, statt eines anderen.

    Man kann also mit solchen einfachen Beschränkungen Fehler vorbeugen.


    Auf dem live server schreib ich zB auch keine DELETE oder UPDATE query ohne LIMIT, wenn ich nur eine Zeile ändern will.

    Einmal nicht aufgepasst, irgendwas in der Bedingung vergessen, und 100000 rows sind weg. :D


    Warum nicht nutzen, was da ist? :)

    Es ist nie gut, mögliche Fehler zu kaschieren!

    Aber das ist eine DELETE query. Du willst doch nicht erst, wenn alles gelöscht ist, feststellen, dass da ein Fehler ist :)


    wodurch die Ergebnisliste fehlerhaft sortiert sein kann und man ein falsches Ergebnis verarbeitet

    Kommt evtl auf den Fall an.

    Bsp user tbl.

    Wenn Du nur einen user laden willst, der per query definition nur einmal vorkommen kann,

    dann macht ORDER BY id ASC LIMIT 1 Sinn,

    denn es kann kein nächster user selectiert werden. Es wird immer der "originale", erste user selectiert.

    $db->query("DELETE FROM Zuege WHERE Zugnummer = '$Nummer'");


    Die Zugnummer wird sicherlich unique sein. Ich empfehle LIMIT 1 an die Query anzuhängen.

    Und, wie schon erwähnt wurde, SQL-Injection fixen!

    SQL
    DELETE FROM `Zuege` WHERE `Zugnummer` = :train_id LIMIT 1;

    Warum LIMIT 1:

    Die Query funktioniert, alles i.O.

    Aber mit der Zeit wird sie erweitert, wird immer komplizierter, und evtl passiert ein Fehler - die Bedingungen betreffen auf einmal mehrere oder alle Zeilen in der db.

    $db = mysqli_connect("example.com", "example", "1234", "example");

    "example.com"

    Ich hoffe, die db ist nicht unnötig übers Internet erreichbar.

    Dort steht dann hoffentlich "127.0.0.1" oder "localhost" :)


    Und wie Stef schon sagte: Verzeichnisschutz ist einer SQL-Injection egal.


    Entweder prepared statements via mysqli, oder PDO (ich empfehle PDO).

    https://www.php.net/manual/de/pdo.prepared-statements.php

    https://www.php.net/manual/de/…t.prepared-statements.php



    Und vergiss strip_tags. Das bring keine Sicherheit. Prepared statements aber schon.


    Wenn eine Anwendung ausschließlich Prepared Statements benutzt, kann sich der Entwickler sicher sein, dass keine SQL-Injection auftreten wird.