flixxgamez: mysql: Login

Hey, ich versuche nun schon mehrere Tage ein einfachen Login für meine Seite zum Laufen zu bringen. Habe schon 6 verschiedene ausprobiert und keine hat irgendwie funktioniert. (Bekam nie einen Error, aber kein Ergebnis)

Frage A: Fällt euch eine Sache mit PHP oder MySQL ein, welche oft Fehler verursacht bei so einer Anwendung, welche ohne alle Dateien hochzuladen zu finden wäre? Denke da an was wie allgemeine mit MySQL und XAMPP oder ähnliches.

Frage B: Hat einer von euch ein sehr einfaches Login-Tutorial, dass einfach aufgebaut ist und kein PHP-Wissen braucht? Es muss "nur" ein Login habe und keine andere Funktion wie Passwort-Reset oder Registration!

Danke flixxgamez!

  1. Hallo,

    ich versuche nun schon mehrere Tage ein einfachen Login für meine Seite zum Laufen zu bringen. Habe schon 6 verschiedene ausprobiert und keine hat irgendwie funktioniert. (Bekam nie einen Error, aber kein Ergebnis)

    Ohne zu sehen was du probiert hast lässt sich natürlich nicht sagen was du falsch gemacht hast, aber wenn du nur eine leere Seite ausgegeben bekommst sollte es helfen display_errors auf on zu setzen (ggf. auch in der .htaccess) und ggf. noch error_reporting voll aufzudrehen.

    Frage A: Fällt euch eine Sache mit PHP oder MySQL ein, welche oft Fehler verursacht bei so einer Anwendung, welche ohne alle Dateien hochzuladen zu finden wäre? Denke da an was wie allgemeine mit MySQL und XAMPP oder ähnliches.

    Da gibt es nicht nur eine Sache, das Raten anzufangen ist wenig sinnvoll – du solltest mal versuchen herauszubekommen was PHP dir für einen Fehler meldet (s.o.).

    Frage B: Hat einer von euch ein sehr einfaches Login-Tutorial, dass einfach aufgebaut ist und kein PHP-Wissen braucht?

    Im Prinzip sollte da schon das Beispiel im Handbuch zu password_verify reichen aber so völlig ohne PHP-Wissen wird wohl auch das nichts werden (v.a. wenn der Hash vorher in der Datenbank gespeichert wird und erst dort geholt werden muss).

    Gruß,
    Tobias

  2. Lieber flixxgamez,

    ich versuche nun schon mehrere Tage ein einfachen Login für meine Seite zum Laufen zu bringen. Habe schon 6 verschiedene ausprobiert und keine hat irgendwie funktioniert. (Bekam nie einen Error, aber kein Ergebnis)

    mit anderen Worten: Du hast keine Ahnung, was Du da tust und probierst geduldig ein copy&paste nach dem anderen durch.

    Kennst Du schon unser Tutorial dazu? Dann wüsstest Du wenigstens ein bisschen, was Du da tust...

    Liebe Grüße

    Felix Riesterer

  3. Fällt euch eine Sache mit PHP oder MySQL ein, welche oft Fehler verursacht bei so einer Anwendung, welche ohne alle Dateien hochzuladen zu finden wäre?

    Ja. Das berühmte Level-8-Problem kennt die Erweiterung auf Level 8a: jemand will etwas sehr spezielles ohne zu wissen (oder genau wissen zu wollen) was er sich da (und warum überhaupt) auf den Hals wünscht: Was zum Teufel willst Du denn überhaupt mit einer Datenbank, wenn Du die Logins nicht verwalten willst

    Hat einer von euch ein sehr einfaches Login-Tutorial, dass einfach aufgebaut ist und kein PHP-Wissen braucht?

    Klar. Dann hat das auch nichts mit PHP zu tun. Bekannt als 'htaccess'. Mit allen Vor- und Nachteilen (z.B. kein Logout.)

    Aber auch darum herum kann man Skripte schreiben.

    1. Aber auch darum herum kann man Skripte schreiben.

      Finger weg davon. Online-Tools zur Erzeugung und Verarbeitung von Passwörtern sind im Allgemeinen nur mit sehr viel Skepsis zu begegnen.

      Das Skript, um das es hier geht, gibt in der Standard-Konfiguration den Inhalt der .htpasswd-Datei aus. Den Inhalt kann dann jeder Mensch sehen und bearbeiten. Wenn man dann noch dem Link am Seitenende zu http://www.fastix.org folgt, wird die URL des Skripts als Referrer an den Seitenbetreiber übertragen. Er wird de facto darüber benachrichtigt, dass das Skript gerade irgendwo im Einsatz ist. Er kann das nutzen, um die Passwort-Hashes auszulesen oder sich einen eigenen Benutzer anzulegen. Das kann auch vollautomatisch innerhalb weniger Millisekunden geschehen.

      Wenn man außerdem die Option FTX_USE_BCRYPT auf 0 setzt, wird ein Pseudozufallsgenerator zur Erzeugung des Salts benutz. Pseudozufallsgeneratoren gelten für kryptografische Zwecke aber nicht als sicher.

      Außerdem ist das Skript anfällig für XSS-Angriffe. Die Funktion PrintUserList maskiert an einer Stelle die Variable $strUser nicht kontextgerecht. Ein Angreifer kann sich das zu Nutze machen und einen Keylogger einschlesuen um so an die Passworte im Klartext kommen. In Kombination mit der ersten Sicherheitslücke ist das die perfekte Hintertür.

      1. Also ich habe mir Deine, diesmal hinreichend sachliche Kritik ernst genommen und eine neue Version aufgelegt.

        Im Einzelnen:

        Das Skript, um das es hier geht, gibt in der Standard-Konfiguration den Inhalt der .htpasswd-Datei aus.

        Ja, das vorzukonfigurieren war natürlich ein Fehler.

        [v] Nichjt die Konfiguration, sondern diese Anzeige überhaupt habe ich vollständig entfernt.

        Wenn man dann noch dem Link am Seitenende zu http://www.fastix.org folgt, wird die URL des Skripts als Referrer an den Seitenbetreiber übertragen.

        [v] Entfernt.

        Er kann das nutzen, um die Passwort-Hashes auszulesen oder sich einen eigenen Benutzer anzulegen.

        Hinter dem „kann“ stand allerdings die relativ unwahrscheinliche Voraussetzung, dass der Benutzer den Schutz nicht aktiviert und vorher auf den Link klickt…

        Wenn man außerdem die Option FTX_USE_BCRYPT auf 0 setzt, wird ein Pseudozufallsgenerator zur Erzeugung des Salts benutz.

        [v] Ich habe daran folgendes geändert: Diese Option gibt es nicht mehr. Es wird jetzt ausschießlich analog zu

        htpasswd -BbnC10 foo passwort
        

        gehasht. Also bcrypt, 2^10 Runden ($2y$10$…).

        Der Pseudozufallsgenerator ist damit weg. Allerdings muss ich hier einwenden, dass bei der alten Hashmethode des Apache (htpasswd ohne die Option -B), bekannt als „Apache-MD5“, die Problematik des schlechten Zufalls wohl um einiges hinter anderen Unzulänglichkeiten zurück tritt (bzw. trat)

        Außerdem ist das Skript anfällig für XSS-Angriffe. Die Funktion PrintUserList maskiert an einer Stelle die Variable $strUser nicht kontextgerecht.

        [v] Erledigt.

        Übigens konnte das nur funktionierten, wenn der Admin sich selbst abschoss.

        Ein Angreifer kann sich das zu Nutze machen und einen Keylogger einschleusen um so an die Passworte im Klartext kommen. In Kombination mit der ersten Sicherheitslücke ist das die perfekte Hintertür.

        Das müsste dann aber schon je nach Verwendung einer der konfigurierten Benutzer (die - je nach Verwendung - selbst Admin sind) oder sogar nur der Admin selbst gewesen sein...

        ToDo:

        Noch ist nicht alles konfigurierbar (e.g. Mindestlänge des Benutzernamens, Erlaubte Zeichen in diesem, ggf. - der Apache nutzt selbst „nur“ 2^10 Runden muss es dann ja auch verstehen- Bcrypt-Runden).

        1. Also ich habe mir Deine, diesmal hinreichend sachliche Kritik ernst genommen und eine neue Version aufgelegt.

          Du wolltest wohl schreiben, du hast diesmal meine hinreichend sachliche Kritik ernst genommen. Haben wir damit nun genug Seitenhiebe verteilt?

          Aber gut, dass du die Sicherheitslücken nun schließt.

          Außerdem ist das Skript anfällig für XSS-Angriffe. Die Funktion PrintUserList maskiert an einer Stelle die Variable $strUser nicht kontextgerecht.

          [v] Erledigt.

          htmlspecialchars ist ein Anfang, aber dein Zielkontext ist JavaScript. Um die XSS-Lücke zu schließen brauchst du json_encode. htmlspecialchars allein hilft hier nicht, weil es keine single Quotes maskiert. Das ließe sich zwar über ein zusätzliches Flag umschiffen, aber json_encode wäre der ordentliche Weg.

          Du hast aber noch ein weiteres Problem. Deine Anwendung ist anfällig für CSRF-Angriffe. Damit kann ein Angreifer quasi beliebige Aktionen im Namen des Bentuzers ausführen. Da hilft auch dein Hinweis an den Nutzer nicht, dass er das Skript nur in einem bereits geschützten Bereich aufrufen darf. Dem Angreifer muss es nur gelingen sein authentifziertes Opfer auf seine Website zu locken.

          1. Deine Anwendung ist anfällig für CSRF-Angriffe. Damit kann ein Angreifer quasi beliebige Aktionen im Namen des Benutzers ausführen.

            Das waren zwar nicht ganz so „beliebige Aktionen“, sondern „lediglich“ das Löschen von anderen Benutzern, aber das reicht ja völlig aus, um Schaden zu verursachen.

            [v] erledigt: Der zu löschende Benutzername wird jetzt nicht mehr per GET übertragen, sondern in ein Cookie mit sehr begrenzter Gültigkeit und Lebensdauer geschrieben. Die Möglichkeit, Cookies zu schreiben wird geprüft.

            htmlspecialchars ist ein Anfang, aber dein Zielkontext ist JavaScript.

            Eigentlich war das gefahrfrei, da die Benuternamen nur aus /[A-Za-z0-9_.-]/ bestehen durften. Aber für den denkbaren Fall, dass die Datei mit Benutzername und Passwort-Hash von Hand oder durch eine konkurrierende Anwendung bearbeitet werden - und weil ich mal sehen wollte zu welchem Ergebnis das führt - habe ich das jetzt an der JS-Stelle so gemacht:

            trim( json_encode( $strUser, JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_TAG ), '"' )
            

            Für das Cookie lasse ich dann durch JS noch Strichpunkte (Semikolons) im Username durch \u003B ersetzen.

            Im HTML-Teil:

            htmlspecialchars( $strUser, ENT_QUOTES )
            

            Mit dem eigentlich unmöglichen Benutzername

            f'<script>alert("hurra");</script>'oo
            

            in der Passwortdatei ergibt das:

            <li class="t2" title="Diesen Benutzer löschen ..." onclick="Ask('f\u0027\u003Cscript\u003Ealert(\u0022hurra\u0022);\u003C\/script\u003E\u0027oo' )">f&#039;&lt;script&gt;alert(&quot;hurra&quot;);&lt;/script&gt;&#039;oo</li>
            

            Weitere Veränderungen:

            • die "Kosten" für den BCRYPT-Algorithmus sind jetzt konfigurierbar - Falls der Apache und PHP irgendwann einmal verschiedene Vorstellungen darüber entwickeln, welche Einstellung von PHP als default benutzt und welche vom Apache akzeptiert wird.

            • Für die Kompatibilät mit denkbaren anderen Anwendungen muss der Benutzername jetzt mit einem Buchstabe beginnen.

            • Die Mindestlänge des Benutzernamens ist konfigurierbar.

            Version 4.1 - für die Quelltextansicht entweder diesen Link nehmen oder bitte reload drücken.

            1. Deine Anwendung ist anfällig für CSRF-Angriffe. Damit kann ein Angreifer quasi beliebige Aktionen im Namen des Benutzers ausführen.

              Das waren zwar nicht ganz so „beliebige Aktionen“, sondern „lediglich“ das Löschen von anderen Benutzern, aber das reicht ja völlig aus, um Schaden zu verursachen.

              Ich sehe nicht welche Gegenmaßnahme du gegen das Anlegen und Bearbeiten von Benutzern ergriffen hast. CSRF-Attacken sind auch mit POST-Anfragen möglich. Ich würde auch nicht dazu raten selbsterfundene Maßnahmen mit JavaScript zu ergreifen. Benutze besser eine der etablierten, und intensiv erprobten Verteidigungs-Mechanismen.

            2. Du hast noch drei weitere XSS-Sicherheitslücken, überall da wo du <?=$_SERVER['PHP_SELF'];?> stehen hast. Die Zeile header( 'Location: ' . $_SERVER['PHP_SELF'] ); braucht auch Aufmerksamkeit. Ich weiß nicht, wie Browser mit fehlerhaften Location-Headern verfahren.

              1. Du hast noch drei weitere XSS-Sicherheitslücken, überall da wo du <?=$_SERVER['PHP_SELF'];?> stehen hast.

                Oh. Das war in 2 von 3 Fallen sogar besser ganz wegzulassen (man muss <form> kein action auf den Weg geben um das selbe Skript zu adressieren).

                Die Zeile header( 'Location: ' . $_SERVER['PHP_SELF'] ); braucht auch Aufmerksamkeit. Ich weiß nicht, wie Browser mit fehlerhaften Location-Headern verfahren.

                13. Mai 2020 - Newflash: „Die Browser können seit sehr vielen Jahren mit relativen (nicht: „fehlerhaften") URLs in Location-Headern wunderbar umgehen. Anderes „Wissen“ ist veraltet.“

                Benutze besser eine der etablierten, und intensiv erprobten Verteidigungs-Mechanismen.

                Ah ja. Eine richtig schöne und große Scriptsammlung.

                Das wäre in meinem Fall so, als würde ich am Nordpol eine riesige Verteidungsanlage errichten, damit $böserFeind nicht mit Infrarotzielköpfen auf die dort befindlichen Eisbrocken schießen kann - und auf diese Weise etwas errichten, auf das es es sich überhaupt zu schießen lohnt. Anders ausgedrückt, behandeln die Jungs von OWASP einen Haufen von Problemen, die mein Skript nicht haben kann - jedenfalls so lange nicht jemand versucht, es in etwas wie Wordpress zu integrieren, Facebookbuttons zu integrieren ider Werbebanner darauf zu pflanzen. Alles Zeug, was ich nicht nur missbillige, sondern explizit verhindere.

                Aber ich habe trotzdem Einiges getan. Ich habe strenge HTTP-Header gesetzt:

                • X-XSS-Protection
                • X-Frame-Options
                • Origin
                • Access-Control-Allow-Origin
                • Content-Security-Policy
                • X-Content-Security-Policy
                • X-WebKit-CSP
                • Referrer-Policy

                sowie das Laden als Frame oder serverseitig mit include/require verhindert. Neu ist aber auch eine Rückfragen beim Umschalten des Verzeichnisschutzes. Ferner ist die Verwaltung auf in der Konfiguatrion bestimmbare Benutzer beschränkbar. Die Farben der Anwendung unterscheiden sich, wenn der Zugriffschutz besteht oder nicht. Und: Vor allen verändernden Aktionen wird der Referer geprüft.

                Fast am wichtigsten: Der Verwender wird nach einem Login jetzt sehr explizit aufgefordert, dass Browserfenster zu schließen um sich "abzumelden". Javascript muss ja für irgendetwas gut sein...

                1. Aber [ich habe trotzdem Einiges getan]

                  Was sich deutlich besser nachvollziehen ließe, könnte man die Versionshistorie verfolgen ;-)

                2. Du hast noch drei weitere XSS-Sicherheitslücken, überall da wo du <?=$_SERVER['PHP_SELF'];?> stehen hast.

                  Oh. Das war in 2 von 3 Fallen sogar besser ganz wegzulassen (man muss <form> kein action auf den Weg geben um das selbe Skript zu adressieren).

                  Und den dritten Fall hast du vergessen. Die XSS-Sicherheitslücke besteht dort weiterhin. Mit XSS-Attacken lassen sich auch Schutzmaßnahmen gegen CSRF-Attacken umgehen. Damit kann ein Angreifer also auch Aufgaben im Namen des Endnutzers ausführen.

                  Zudem hast du noch eine weitere Lücke eingebaut. In der Zeile trigger_error( 'Nicht erwarteter Referer: ' . $_SERVER['HTTP_REFERER'] . ' ' . $str, E_USER_ERROR ); wird der HTTP_REFERER nicht maskiert. Wenn der Server so konfigugiert ist, dass er PHP-Fehler ausgibt, ermöglich das einem Angreifer JavaScript einzuschleusen.

                  Die Zeile header( 'Location: ' . $_SERVER['PHP_SELF'] ); braucht auch Aufmerksamkeit. Ich weiß nicht, wie Browser mit fehlerhaften Location-Headern verfahren.

                  13. Mai 2020 - Newflash: „Die Browser können seit sehr vielen Jahren mit relativen (nicht: „fehlerhaften") URLs in Location-Headern wunderbar umgehen. Anderes „Wissen“ ist veraltet.“

                  Du hast glaube ich nicht verstanden, worauf ich hinaus wollte. Es ging nicht um relative URLs, es ging darum, dass $_SERVER['PHP_SELF'] vor einer Ausgabe kontextgerecht behandelt werden muss, weil ein Angreifer sonst einen Syntax-Fehler in dem entsprechenden Header auslösen kann. Wozu der Syntax-Fehler führt, und ob das ein Sicherheitsrisiko birgt ist Sache der Browser-Implementierung. Aber das hast du ja nun angepasst, also geschenkt.

                  Benutze besser eine der etablierten, und intensiv erprobten Verteidigungs-Mechanismen.

                  Ah ja. Eine richtig schöne und große Scriptsammlung.

                  Von einer Skript-Sammlung war nie die Rede. Ich wollte dich dazu anleiten eine der erprobten Maßnahmen gegen CSRF-Angriffe zu implementieren und nicht dein eigenes Süppchen zu kochen. Viele vermeintliche Gegenmaßnahmen haben sich schon als unzureichend herausgestellt. Du hast dich auf den selben Pfad begeben und dabei offensichtlich einige Angriffsvektoren übersehen, weil du dem Irrglauben verfallen bist, dass POST-Anfragen gegen CSRF-Attacken immun seien. Die Lektüre, die ich dir verlinkt habe, erklärt warum das nicht der Fall ist. Aber auch das hast du im zweiten Anlauf nun korrigieren können, also geschenkt. Wichtig ist aber, dass du die XSS-Sicherheitslücken schließt, weil sich sonst dein CSRF-Schutz umgehen lässt.

                  Das wäre in meinem Fall so…

                  Bleiben wir doch beidem Fall: Fakt ist, dass du einen Passwort-Manager anbietest, bei dem wir jetzt rund ein Dutzend Sicherheitslücken aufgedeckt haben. Die Lücken ermöglichten im schlimmsten Fall einem Angreifer Passworte im Klartext auszulesen, Passwort-Hashes auszulesen, eigene Benutzer anzulegen, zu löschen, zu bearbeiten und den Passwort-Schutz zu deaktivieren oder zu aktivieren, um andere Benutzer auszuschließen.

                  Aber ich habe trotzdem Einiges getan. Ich habe strenge HTTP-Header gesetzt:

                  Und doch hilft das, was du da getan hast, nicht auf magische Weise XSS-Angriffe zu vermeiden. Du musst schon verstehen, was du da tust. Du setzt bswp. script-src 'self' 'unsafe-inline'. Das ist ein Türöffner für XSS-Angriffe. Hast du die Spezifikation gelesen? unsafe-inline sollte ausdrücklich nicht gesetzt werden. Zitat:

                  In either case, developers SHOULD NOT include either 'unsafe-inline', or data: as valid sources in their policies. Both enable XSS attacks by allowing code to be included directly in the document itself; they are best avoided completely.

                  Und wie anfangs erläutert hast du mindestens noch zwei genau solcher Sicherheitslücken.

                  1. In der Zeile trigger_error( 'Nicht erwarteter Referer: ' . $_SERVER['HTTP_REFERER'] . ' ' . $str, E_USER_ERROR ); wird der HTTP_REFERER nicht maskiert. Wenn der Server so konfigugiert ist, dass er PHP-Fehler ausgibt, ermöglich das einem Angreifer JavaScript einzuschleusen.

                    Auf Produktivsystemen werden keine Fehlermeldungen ausgegeben.

                    Die Zeile header( 'Location: ' . $_SERVER['PHP_SELF'] ); braucht auch Aufmerksamkeit. Ich weiß nicht, wie Browser mit fehlerhaften Location-Headern verfahren.

                    13. Mai 2020 - Newflash: „Die Browser können seit sehr vielen Jahren mit relativen (nicht: „fehlerhaften") URLs in Location-Headern wunderbar umgehen. Anderes „Wissen“ ist veraltet.“

                    Es geht nicht um relative URLs, es geht darum, dass $_SERVER['PHP_SELF'] vor einer Ausgabe kontextgerecht behandelt werden muss, weil ein Angreifer sonst einen Syntax-Fehler in dem entsprechenden Header auslösen kann.

                    14.5.2020 Newsflash: Das PHP Handbuch sagt zu $_SERVER['PHP']

                    'PHP_SELF'
                        Der Dateiname des aktuell ausgeführten Skripts, relativ zum Document Root.
                    

                    Wie soll er das tun?

                    Ich habe das ausgeschlossen, in dem ich die Skriptausführung abbreche, sobald FILE (wirklich aktelle Datei bei Inkludes) und $_SERVER['PHP_SELF'] (ggf. includierende Datei) nicht übereinstimmen.

                    Selbst wenn ein Angreifer also irgendwie eine andere Datei so manipulieren kann, dass diese mein Script includiert, dann scheitert er an diesem Punkt. Das ist eigentlich schon akademisch - aber bitte - ich habe das verhindert.

                    Jetzt erkläre mir doch bitte, wie ein Angreifer den Dateiname oder den Pfad so manipulieren können soll. Und warum er das tun sollte, wenn er das kann. Denn genau dann könnte er auf dem Server sehr viel mehr sehr viel einfacher.

                    Du setzt bswp. script-src 'self' 'unsafe-inline'. Das ist quasi der Türöffner für XSS-Angriffe

                    Aber nur in Projekten, bei denen „usergeneratet content“ oder anderer fremder Content vorkommen kann und ggf. nicht korrekt behandelt wird. Das ist hier nicht der Fall, die Benutzernamen werden ohne Kommentar* verworfen wenn was anderes als [A-Za-z0-9._-] drin ist (*:Die werden ja schon zuvor vom Browser geprüft). Die Passwörter nirgends wiedergegeben.

                    Das Skript erfüllt die Voraussetzungen für eine solche XSS-AtTacke also nicht. Gewiss nicht bei $_SERVER['PHP_SELF'].

                    Immerhin müsste man einem Pfadname wie

                    /foo/<script>alert("Böse");</script>/bar
                    

                    anlegen. Das macht eher kein Besitzer einer Webseite. Und wie schon gesagt: Ein Angreifer, der das kann, kontrolliert den Server bereits. Damit ist das keine Sicherheitslücke mehr, sondern so oder so eine rein akademische und ziemlich krude erscheinende Vorstellung. Warum, BITTE, sollte er DAS tun?

                    Die Idee, hier tonnenweise irgendwelche Skripte, womöglich noch fremden Servern, zu inkludieren um die Sicherheit zu „erhöhen“, ist in dem Fall völlig daneben. Genauer: Nicht mehr hyperliquid sondern kontraproduktiv. Noch genauer: Nur im besten Fall unschädlich.

                    1. In der Zeile trigger_error( 'Nicht erwarteter Referer: ' . $_SERVER['HTTP_REFERER'] . ' ' . $str, E_USER_ERROR ); wird der HTTP_REFERER nicht maskiert. Wenn der Server so konfigugiert ist, dass er PHP-Fehler ausgibt, ermöglich das einem Angreifer JavaScript einzuschleusen.

                      Auf Produktivsystemen werden keine Fehlermeldungen ausgegeben.

                      Es sollten keine Fehlermeldungen ausgegeben werden. Du kannst es nicht wissen, die Frage ist, wieso das Risiko überhaupt eingehen, wenn es sich so leicht beheben lässt.

                      14.5.2020 Newsflash: Das PHP Handbuch sagt zu $_SERVER['PHP']

                      'PHP_SELF'
                          Der Dateiname des aktuell ausgeführten Skripts, relativ zum Document Root.
                      

                      Die Dokumentation ist an der Stelle ungenau. Einer der Kommentare erwähnt, dass sich die Variable spoofen lässt. Alles was ein Angreifer tun muss, ist dem Endnutzer einen manipulierten Link unterzuschieben.

                      Ich habe das ausgeschlossen, in dem ich die Skriptausführung abbreche, sobald FILE (wirklich aktelle Datei bei Inkludes) und $_SERVER['PHP_SELF'] (ggf. includierende Datei) nicht übereinstimmen.

                      Wie ich das sehe überprüfst du realpath( __FILE__ ) !== realpath( $_SERVER['SCRIPT_FILENAME'] ) ). Also SCRIPT_FILENAME und nicht PHP_SELF. Ich würde mich bei der Ausgabe von Variablen auch nicht auf einen Test verlassen, der ein paar Hundert Zeilen vorher stattfindet. Wenn der Test sich mal ändert, hast du den Salat. Was ist so schlimm daran Werte ordentlich vor der Ausgabe zu maskieren?

                      Du setzt bswp. script-src 'self' 'unsafe-inline'. Das ist quasi der Türöffner für XSS-Angriffe

                      Aber nur in Projekten, bei denen „usergeneratet content“ oder anderer fremder Content vorkommen kann und ggf. nicht korrekt behandelt wird.

                      Den Fall haben wir jetzt ja nun mehrfach gehabt. Was ist so schlimm daran der Empfehlung des W3C zu folgen eine striktere Policy zu implementieren? Dann hättest du wirklich einen effektiveren Schutz gegen XSS-Angriffe.

                      1. Die Dokumentation ist an der Stelle ungenau. Einer der Kommentare erwähnt, dass sich die Variable spoofen lässt. Alles was ein Angreifer tun muss, ist dem Endnutzer einen manipulierten Link unterzuschieben.

                        Tatsächlich. Habe das gesucht und gefunden.

                        Test:

                        <html> <?php header ('Content-type:text/html');error_reporting(E_ALL); ini_set("display_errors", 1); trigger_error( 'foo '.$_SERVER['PHP_SELF'] . ' bar') ; ?> </html>

                        klappt, wenn man http://example.com/<script>alert('böse')</script> aufruft.

                        Ist jetzt nicht dringend - aber ich sollte wohl mal was mit grep -R machen.

                  2. Zudem hast du noch eine weitere Lücke eingebaut. In der Zeile trigger_error( 'Nicht erwarteter Referer: ' . $_SERVER['HTTP_REFERER'] . ' ' . $str, E_USER_ERROR ); wird der HTTP_REFERER nicht maskiert. Wenn der Server so konfiguriert ist, dass er PHP-Fehler ausgibt, ermöglich das einem Angreifer JavaScript einzuschleusen.

                    Schaun wir mal:

                    <?php
                    
                    error_reporting(E_ALL);
                    ini_set("display_errors", 1);
                    include '<script>alert("Böse");</script>';
                    

                    Ausgaben im Browser:

                    Warning: include(<script>alert("Böse");</script>): failed to open stream: No such file or directory in /var/www/seminar/htdocs/test/test.php on line 5
                    
                    Warning: include(): Failed opening '<script>alert("Böse");</script>' for inclusion (include_path='.:/usr/share/php') in /var/www/seminar/htdocs/test/test.php on line 5
                    

                    Warum kommt das alert nicht?

                    Quelltext:

                    <br />
                    <b>Warning</b>:  include(&lt;script&gt;alert(&quot;Böse&quot;);&lt;/script&gt;): failed to open stream: No such file or directory in <b>/var/www/seminar/htdocs/test/test.php</b> on line <b>5</b><br />
                    <br />
                    <b>Warning</b>:  include(): Failed opening '&lt;script&gt;alert(&quot;Böse&quot;);&lt;/script&gt;' for inclusion (include_path='.:/usr/share/php') in <b>/var/www/seminar/htdocs/test/test.php</b> on line <b>5</b><br />
                    

                    Objektive Gegenprobe, denn der Browser könnte das manipuliert haben. Also noch :

                    wget -O- http://127.0.0.1/test/test.php
                    

                    ... zeigt exakt obigen Quelltext.

                    Fazit:

                    • PHP behandelt selbst alle Strings in Fehlermeldungen.

                    Wenn der Server so konfiguriert ist, dass er PHP-Fehler ausgibt, ermöglich das einem Angreifer JavaScript einzuschleusen.

                    Das ist also nicht „falsch verstanden“, sondern falsch.

                    1. Mit

                      <?php
                      
                      error_reporting(E_ALL);
                      ini_set("display_errors", 1);
                      trigger_error( '<script>alert("böse");</script>', E_USER_ERROR );
                      

                      klappt das. Soll heißen: Bei selbst generierten Fehlermeldungen entschärft PHP nicht und Du hättest recht, wenn die Ausgabe der Fehler vorgesehen wäre.

                    2. Schaun wir mal:

                      <?php
                      
                      error_reporting(E_ALL);
                      ini_set("display_errors", 1);
                      include '<script>alert("Böse");</script>';
                      

                      Ich weiß nicht, was du mit dem include zeigen willst. Der Punkt ist, dass du den Referrer in HTML ausgibst ohne ihn zu maskieren. Das kann für einen Angriff genutzt werden, indem man dem Opfer einen manipulierten Link unterschiebt. Das Opfer folgt dem Link, die Seite leitet das Opfer dann um, um den Referrer mit Schadcode zu füllen und leitet das Opfer schließlich auf deinen Passwort-Manager. Dort wird der Schadcode in HTML ausgegeben und ausgeführt.

                      1. Ich weiß nicht, was du mit dem include zeigen willst.

                        Ich dachte doch WIRKLICH, dass PHP bei der Ausgabe von Fehlermeldungen konsequent ist:

                        • Beim gezeigten include '<script>alert("Böse");</script>'; wird das Zeug maskiert.

                        Dem ist aber NICHT so:

                        • Bei trigger_error('<script>alert("Böse");</script>') wird das Skriptzeug ungerührt ausgegeben. Es gibt dafür nicht einmal eine Option für das Maskieren. Auch ini_set("display_errors", 1); (bzw. die php.ini) bietet hierzu nichts an.

                        Allerdings habe ich in der Dok die schweinchenfarbige Box übersehen:

                        Warnung

                        HTML-Entities in der error_msg werden nicht automatisch escaped. Wenn die Fehlermeldung in einem Browser ausgegeben werden soll so sollten Sie diese in diesem Fall selbst mit htmlentities() vorverarbeiten.

                        Asche auf mein Haupt…

                        Was ist so schlimm daran der Empfehlung des W3C zu folgen eine striktere Policy zu implementieren?

                        Nun, nachdem ich Inline-CSS und Inline-JS in zwei Blöcken im Header stehen und mit einer „nonce“ versehen habe, konnte ich die Policy strikter formulieren:

                        X-XSS-Protection: 1; mode=block
                        X-Frame-Options: deny
                        Origin: http://localhost
                        Access-Control-Allow-Origin: http://localhost
                        Content-Security-Policy: default-src 'none'; style-src 'nonce-d0e884cde3afa316d0bc3cce'; script-src 'nonce-1d76a2c9c922642ade6bc1df'; img-src data:; form-action 'self'; base-uri 'self'
                        X-Content-Security-Policy: default-src 'none'; style-src 'nonce-d0e884cde3afa316d0bc3cce'; script-src 'nonce-1d76a2c9c922642ade6bc1df'; img-src data:; form-action 'self'; base-uri 'self'
                        X-WebKit-CSP: default-src 'none'; style-src 'nonce-d0e884cde3afa316d0bc3cce'; script-src 'nonce-1d76a2c9c922642ade6bc1df'; img-src data:; form-action 'self'; base-uri 'self'
                        Referrer-Policy: strict-origin-when-cross-origin
                        

                        Außerdem hat die Version 4.3 noch ein paar Neuerungen.

                        Was leider nicht funktionierte waren kryptographisch strenge Vorschläge für die Passwörter - da hängt der Browser (Firefox, Chromium unter Ubuntu ) ab dem 3. oder 4. Passwort und wartet (wohl auf Entropie) - Ich musste also auf das schnöde Math.random() zurückfallen, habe das aber mit ein paar, von den aktuellen Microsekunden abhängigen Leerabrufen „aufgebessert“ und bei der Gelegenheit auch für einen hübschen optischen Effekt gesorgt. Auch die Cookies werden jetzt wesentlich strikter gehandhabt: SameSite=Strict bei http und SameSite=Strict; secure bei https.

                        (In der Voreinstellung ist http nur für den localhost bzw. das Netz 127.0.0.0/8) erlaubt.

                        Bevor das auffällt: Das Favicon ist jetzt fest (base64-kodiert) integriert. Nicht, dass es da irgendwelche Probleme mit Weiterleitungen, fremden Servern oder dergleichen gibt.

  4. Hi,

    in welcher Schicht genau funktioniert dein Skript denn nicht?

    • "Anmeldung" beim System, also z.B. Erzeugung eines Session-Tokens mit gültiger Anmeldeinformation in der Session
    • Anmeldung des MySQL-Requesters in der Skriptinstanz bei der Datenbank
    1. Die Version des von PHP benutzten Requesters muss zur Datenbankversion passen.
    2. Wird evetuell SSL benutzt?
    3. wurde in der Datenbank ein passender User z.B. "readonly@localhost" vereinbart? Wurden die Anmeldedaten (Username und Passwort) außerhalb der DocumentRoot hinterlegt unx konnten sie mit requireonce eingebunden werden?

    Du wirst nicht drum herum kommen, dir die unterschiedlichen Schichten einzeln zum Debuggen vorzunehmen.

    Dabei helfen Dir bestimmt Viele hier gerne genauso geduldig, wie Du fragst.

    LG Ein Mitleser