Raffi: Anmeldung mit PHP

Hallo Leute

Ich will folgendes realisieren:

Eine Eingabemaske, wo der Benutzer Username und PW eingeben kan.
Dann drückt er auf eine Submit button und wir per PHP direkt mit einer Adresse verbunden (in einem geschützen Unterverzeichnis) Wie kann ich nun machen, dass sich der PHP Script mit den eingegebenen Informationen am Unterverzeichnis "anmeldet"?

Gruss
Raffi

  1. Hi!

    Ich will folgendes realisieren:

    Eine Eingabemaske, wo der Benutzer Username und PW eingeben kan.
    Dann drückt er auf eine Submit button und wir per PHP direkt mit einer Adresse verbunden (in einem geschützen Unterverzeichnis) Wie kann ich nun machen, dass sich der PHP Script mit den eingegebenen Informationen am Unterverzeichnis "anmeldet"?

    Mit einer Datenbank in der Du Adresse username und password speicherst.
    Eíngabemaske ein einfaches HTML Formular. Dann fragst Du nach der EIngabe nach Prüfung die zum Username gehörende UserID ab, und gibst die dann immer weiter, mit der kannst Du dann jederzeit an alle Daten des eingeloggten Users gelangen.

    Grüße
      Andreas

    1. Ich meine ein schon geschützes Unterverzeichnis, wo so ein Anmeldefenster im Browser erscheint...das will ich übersteuern...wie mach ich das?

      1. Moin!

        Ich meine ein schon geschützes Unterverzeichnis, wo so ein Anmeldefenster im Browser erscheint...das will ich übersteuern...wie mach ich das?

        Garnicht. Da werden Mechanismen benutzt, die unbedingt einen Browserdialog erfordern, der NICHT von der besuchten Seite abhängig ist. Denn der BROWSER muß wissen, welche Daten (Username, Passwort) der Benutzer eingibt, und diese bei JEDEM Request (also jede Seite, jedes Bild, jedes Skript aus dem geschützten Verzeichnis) mitsenden.

        PHP kann lediglich auf die eingegebenen Daten zugreifen und so feststellen, ob sie gültig sind und der Zugriff gewährt werden darf. Aber um den Dialog kommst du (bei dieser HTTP-Authentifizierung) nicht herum.

        - Sven Rautenberg

        1. Kann ich das dem Browser nicht wie bei FTP irgendwie mitegeben:

          ftp://USERNAME:PASSWORT@ftp.SERVER.ch

          Gruss
          Raffi

          1. Moin!

            Kann ich das dem Browser nicht wie bei FTP irgendwie mitegeben:

            ftp://USERNAME:PASSWORT@ftp.SERVER.ch

            Du kannst einen Usernamen mitgeben (das sind die "@-Domains"), aber kein Paßwort. Bzw. du kannst das Paßwort zwar auch mitgeben, aber das steht in keinem Standard drin, und muß nicht funktionieren.

            Ich verstehe auch nicht ganz, was du gegen den Eingabedialog hast. Das ist ein Standard, die User kennen das, und du hast die Gewißheit, daß es _sicher_ ist. Alle selbstgestrickten Methoden können Löcher haben und so User hereinlassen, die nicht reinkommen sollen.

            - Sven Rautenberg

            1. Also es geht um folgendes:

              Ich habe diverse Websiten bei einem Provider gehostet. Der Setzt eine WebMail software ein, bei der man sich per Anmeldebildschirm anmelden muss. Die WEbmail Adresse lautet: http://www.DOMAIN.ch:2095

              Ich möchte nun auf unserer Website einen Anmeldedialog generieren, wo der User die Mailadresse eingibt, und dann automatisch weitergeleitet und beim Webmail angemeldet wird.

              Hat jemand eine idee?

              1. Moin

                Hat jemand eine idee?

                Ja. Bau dir einen HTTP-Client in PHP als Wrapper um das Webmaildings herum. Der kann das Passwort wie immer du willst vom Benutzer kriegen und es an das Webmaildings weiterreichen.

                Als Funktionen brauchst du dazu fsockopen(), fread(), fwrite(), fpassthru(), den einen oder anderen regexp, Kentniss von RFC 2616 und ein bisschen Phantasie.

                Alternative: Bitte deinen Provider dir den Source des Webmaildings zu geben, modifizier es und bitte deinen Provider deine modifizierte Variante zu installieren.

                --
                Henryk Plötz
                Grüße aus Berlin

                1. Hallo

                  Klingt gut...leider hab ich keine Ahnung noch Fantasie wie ich das realisiern soll...gibts da irgendwo ein Beispiel?

                  Gruss
                  Raffi

                  1. Moin

                    Klingt gut...leider hab ich keine Ahnung noch Fantasie wie ich das realisiern soll...gibts da irgendwo ein Beispiel?

                    Nicht dass ich wüsste. Ich kann jedoch die Idee mal _grob_ skizzieren.
                    Also du hast ein PHP-Skript, dass wir jetzt einfach mal bla.php nennen. Ausserdem hast du deinen Server so konfiguriert, dass http://deinserver/bla.php/lalala funktioniert, bla.php aufruft und /lalala in $PATH_INFO legt (das sollte eigentlich default sein, zumindest bei PHP als Apache-Modul).

                    Dann sieht dein Skript ungefähr so aus (+/- ein paar Features)

                    <?php

                    // *** Erstmal Benutzername und Passwort vom User erfragen.
                     // Damit das ganze in diesem Beispiel einfach wird, nehm ich $PHP_AUTH_USER und $PHP_AUTH_PW, du kannst das sicherlich trivial auf ein Anmeldeformular ummünzen

                    $pw = $PHP_AUTH_PW;
                     $user = $PHP_AUTH_USER;
                     if($pw == "" || $user == "") {
                      header('WWW-Authenticate: Basic realm="Mein lustiger Webmailverbieger"');
                      header("HTTP/1.0 401 Unauthorized");
                      die("Bitte Username/PW angeben");
                     }

                    // *** Nun da wir die nötigen Daten haben, machen wir eine Verbindung auf
                     $fh = fsockopen("www.domain.ch", 2095, $errno, $errstr, 10);
                     if(!$fh) die("Verbindung konnte nicht aufgebaut werden. Fehlerursache ($errno) $errstr. (Wenn keine Fehlerursache angegeben wird, ist wahrscheinlich die Namensauflösung schuld)");

                    // *** OK, wir senden eine einfache HTTP-Anfrage
                     // Ich hab mir grad überlegt, dass $PATH_INFO doch nicht das gelbe vom Ei ist
                     $mypathinfo = substr($REQUEST_URI, strlen($SCRIPT_NAME));
                     if($mypathinfo == "") {
                      // Hmm, der Browser sollte wenigstens / an den Skriptnamen anhängen, sonst geht das mit den unten erwähnten relativen Referenzen schief
                      header("Location: bla.php/");
                      exit();
                     }

                    // Abfrage zusammenbauen
                     $abf = "GET ".$mypathinfo." HTTP/1.0\r\n";
                     $abf .= "Authorization: Basic ".base64_encode($user.":".$pw)."\r\n";

                    // Jetzt die Header die der Browser gesendet hat noch dranhängen, ausgenommen den Authorization-Header, den der Browser extra für uns gesendet hat, sowie einen evt. Keep-Alive Header, da wir noch kein Keep-Alive können

                    $abf .= "Connection: Close\r\n"; // Kein Keep-Alive

                    $hdr = getallheaders();
                     foreach($hdr as $hdrnam => $hdrval)
                      if($hdrnam != "Authorization" && $hdrnam != "Connection")
                       $abf .= $hdrnam . ": " .$hdrval . "\r\n";

                    // Anmerkung: Wenn dein Zielrechner und der Rechner auf dem dieses Skript läuft nicht identisch sind, musst du höchstwahrscheinlich auch noch den Host:-Header ändern

                    $abf .= "\r\n";

                    // Abfrage senden
                     fwrite($fh, $abf);

                    $meinehdr = Array(); // Ordentlich Leermachen
                     $status = ""; $ctype = "";

                    // Ergebnis einlesen
                     do { // Hauptschleife für die Header
                      $line = "";
                      while($tmp = fgets($fh, 1024)) { // Unterschleife zum Einlesen beliebig langer Zeilen
                       $line .= $tmp;
                       if(strlen($tmp) < 1024 || $tmp[1023] == "\n") break; // Eine Zeile eingelesen
                      }

                    // Lasst uns herausfinden, was der Server da so sendet
                      $line = trim($line); // Überflüssige Whitespace in die Tonne
                      if(preg_match("!^HTTP/\d.\d\s+(\d{3})!", $line, $matches)) {
                       // Das ist eine HTTP-Statuszeile
                       if($matches[1] == "401") {
                        // Tja, das Passwort/der Benutzername/die Kombination ist wohl falsch
                        // Hier dein Anmeldeformular nochmal ausgeben (dafür willst du natürlich eine Funktion bauen)
                        header('WWW-Authenticate: Basic realm="Mein lustiger Webmailverbieger"');
                        header("HTTP/1.0 401 Unauthorized");
                        die("Bitte Username/PW angeben");
                       }
                       // Der Rest ist wohl ok
                       $status = $matches[1]; // Status merken
                       $meinehdr[] = $line; // Header merken
                      } else if(preg_match("!^([^:]*):\s+(.*)$!", $line, $matches)) {
                       // Das ist ein sonstiger Header, da interessiert uns später nur der Content-Type
                       if($matches[1] == "Content-Type") $ctype = $matches[2];
                       $meinehdr[] = $line;
                      } else if($line == "") break; // Eine Leerzeile bedeutet das Ende der Header, die Schleife kann beendet werden
                     } while(!feof($fh));

                    // Die vorher gespeicherten Header ausgeben
                     foreach($meinehdr as $meinhdr) header($meinhdr);

                    // Wenn der Content-Typ nicht text/html, sondern zum Beispiel ein Bild, ist, dann jetzt einfach den Rest der übertragenen Daten ausgeben und dieses Skript beenden
                     if($ctype != "text/html") {
                      fpassthru($fh);
                      exit;
                     }

                    // Jetzt wirds lustig. Wir lesen den Rest in einen String ein, um dann die Links und Bildreferenzen etc. zu verwurschteln

                    $inhalt = "";
                     while(!feof($fh) && $tmp = fread($fh, 2048)) $inhalt.=$tmp;

                    // Das herausfinden von Verweisen etc. ist leider nicht ganz so einfach wie man meinen möchte. Wenn du es allgemein haben willst, findest du im Forumsarchiv einen RegExp der alles findet was wie ein gültiger URL aussieht (such mal nach "prospero", das Wort sollte nur in diesem Regexp - und einigen Verweisen darauf - vorkommen). Das gefundene kannst du dann umschreiben. Ich wähle hier mal den Weg, alles herauszusuchen was wie eine Referenz in HTML aussieht und das gegebebenfalls umzuschreiben. Wenn in deinem Zielskript noch andere Sachen (etwa in JavaScript) vorkommen, musst du diese RegExpe erweitern oder auf die allgemeine Variante umsteigen.
                     // Wahrscheinlich reicht es aus, nur absolute Verweise umzubauen, die relativen müsste der Browser schon von selbst in den virtuellen URL-Baum deines Skriptes einbauen können

                    $quellregexps = Array('!<a(.*)href\s*=\s*"(?:http://www.mydomain.ch:2095)?/([^"]*)"(.*)>!Ui',
                                           '!<img(.*)src\s*=\s*"(?:http://www.mydomain.ch:2095)?/([^"]*)"(.*)>!Ui'); // BTW: Wenn der Autor des anderen Skriptes unfähig war seine href-Parameter in " einzuschliessen, dann darfst du ihn gerne hauen und kannst diesen Ansatz vergessen
                     $zielregexps = Array('<a\1href="http://deinserver/bla.php/\2"\3>',
                                           '<img\1src="http://deinserver/bla.php/\2"\3>');

                    $inhalt = preg_replace($quellregexps, $zielregexps, inhalt);

                    echo $inhalt;

                    ?>

                    Hmmpf, aus "grob" ist wohl nix geworden. Immerhin habe ich das Skript grade lokal getestet und mit normalen Webseiten funktioniert es (erstaunlicherweise :) völlig problemlos. Wie gesagt, bei Frames oder JavaScript-Spielereien musst du die Regulären Ausdrücke anpassen. Ausserdem müsste man sich auch noch mit Location:-Headern befassen, die das andere Skript zum Umleiten sendet, und deren Ziele ebenfalls auf den Aufruf deines Skriptes umbiegen.

                    --
                    Henryk Plötz
                    Grüße aus Berlin

                    1. Moin

                      $inhalt = preg_replace($quellregexps, $zielregexps, inhalt);

                      ^
                      Das muss natürlich $inhalt heissen.

                      --
                      Henryk Plötz
                      Grüße aus Berlin

      2. Moin

        Ich meine ein schon geschützes Unterverzeichnis, wo so ein Anmeldefenster im Browser erscheint...das will ich übersteuern...wie mach ich das?

        Indem du das Verzeichnis entschützt und die ganze Authentifikationssache in PHP mit Formularen, Sessions und wozu du sonst noch so lustig bist, durchführst. Selbstverfreilich dürfen dann in dem Verzeichnis keine weiteren Sachen ausser PHP-Skripten - die den Schutz auch implementieren - liegen (oder alles was nicht PHP ist, muss mit einem deny all in der .htaccess versehen werden) und du musst alle Zugriffe auf irgendeine nicht-phpskriptige Ressource deines geschützten Bereiches durch dein PHP-Skript tunneln, so etwa wie:
        <?php
         if(!tuewasimmernötigistumdenuserzuauthentifizieren()) {
          einlustigesanmeldeformularausgeben();
          exit;
         }
         if($holmich=="seite1") readfile("/ein/pfad/ausserhalb/des/docroot/oder/mit/deny_all/versehen/seite1.html");
         else if($holmich=="bild1") {header("Content-Type: image/jpeg"); readfile("/dito/für/deine/bilder/bild1.jpg");}
         // etc.
        ?>

        Wobei dann in seite1.html Verweise auf Bilder ungefähr so aussehen: <img src="deinlustigesauthscript.php?holmich=bild1">

        Bitte sieh davon ab ein <?php if(userauthentifizieren()) readfile($eineunvalidiertevomuservorgegebenevariable); else dasbereitserwähnteanmeldeformularausgeben(); ?> zu machen. Das wirkt sich negativ auf dein Karma und vor allem die Sicherheit deines Servers und/oder die deiner werten Mitgehosteten aus.

        Das alles kannst du mit dem einen oder anderen gut gezielten RegExp, deinem Freund $PATH_INFO und einer Menge an Intelligenz und/oder Spielzeit natürlich auch schön, einfach und halbwegs universell hinkriegen. Oder du nimmst einfach die schon fertige .htaccess-Lösung.

        --
        Henryk Plötz
        Grüße aus Berlin