Clemens: [und PHP] HTTP-Request mit Login/PW -> Statuscode herausfinden

Hallo Leute!

Nachdem es nun schon wieder hell wird und ich mir die Nacht umsonst um die Ohren gehaut habe, muss ich euch - wieder mal - um Hilfe bitten, ich komme einfach nicht weiter:

Die Vorgeschichte zu meinem Problem findet ihr hier:
http://forum.de.selfhtml.org/archiv/2001/5/24373/#m126904

Kurz zusammengefaßt geht es darum, dass ich mittels eines HTTP-Request incl. Authorizations-Daten (für .htaccess/.htpasswd) den zurückgelieferten Status-Coode (200 OK oder 401 Unauthorisized) herausfinden möchte, um damit die Login/Passwort-Kombination zu prüfen.

Das Problem ist: Irgendwie fehlt mir da der Durchblick, jedenfalls habe ich mit den obigen Angaben und einiger anderen Seiten im Web folgenden Code zusammenbekommen:

<?php
$fp=fsockopen("www.domain.de",80);
fwrite($fp, "POST /geschuetzt/index.html HTTP/1.1\r\n");
fwrite($fp, "Authorization: Basic " .base64_encode("username:passwort"). "\r\n");
fwrite($fp, "Host: http://www.domain.de \r\n\r\n");
$status=fgets($fp,200);
fclose($fp);

echo $status;
?>

Allerdings gibt dieser Code nur folgendes aus:
HTTP/1.1 400 Bad Request

Es sollte aber 200 OK oder 401 Unauthorisized sein! Was ist da im Skript falsch?

Wenn man irgendwie den Status-Code herausbekommt, gibt es noch das Problem zum geschützten Verzeichnis mit bekanntem PW/Login weiterleiten. Aber wie mache ich das? Mit http://login:pw@www.domain.de/geschuetzt/ geht es nicht, da dies nicht RFC-konform ist (RFC 1738, Abschnitt 3.3: http://rfc.net/rfc1738.html). Wenn ich es ähnlich wie oben mache, merkt sich der Browser das vermutlich nicht, einfach in $PHP_AUTH_USER und $PHP_AUTH_PW schreiben bringt auch nichts, scheinbar übernimmt PHP nur die Daten vom Realm, nicht aber umgekehrt. Kann ich z.B. das realm per Skript füttern?

Ich habe überhaupt keine Ahnung wo ich ansetzen kann (vielleicht habe ich auch keine von HTTP1.1 :-)

Clemens

  1. Moin,

    Allerdings gibt dieser Code nur folgendes aus:
    HTTP/1.1 400 Bad Request

    Lass dir mal den weiteren Inhalt ausgeben da steht üblicherweise genauer drin was falsch ist.
    Drei Vermutungen:
    1. Du bist auf einem System gelandet dass zwei Bytes in \n drin hat und ein Dreibytezeilenendezeichen ist dem Webserver wohl zu bunt. Versuch mal \x0d\x0a als Zeilenendezeichen.
    2. HTTP/1.1 ist evt. nicht was du brauchst v.a. da der Server dann automatisch Keep-Alive machen will, versuch mal HTTP/1.0.
    3. POST ist evt. nicht das was du willst (bin mir zwar nicht sicher, aber es könnte sein dass dann ein Body erwartet wird), versuch mal GET.

    Wenn man irgendwie den Status-Code herausbekommt, gibt es noch das Problem zum geschützten Verzeichnis mit bekanntem PW/Login weiterleiten. Aber wie mache ich das? Mit http://login:pw@www.domain.de/geschuetzt/ geht es nicht, da dies nicht RFC-konform ist (RFC 1738, Abschnitt 3.3: http://rfc.net/rfc1738.html). Wenn ich es ähnlich wie oben mache, merkt sich der Browser das vermutlich nicht, einfach in $PHP_AUTH_USER und $PHP_AUTH_PW schreiben bringt auch nichts, scheinbar übernimmt PHP nur die Daten vom Realm, nicht aber umgekehrt.

    Du kriegst die Username/Passwortdaten nicht in den Client ohne dass der User sie eingibt (hatten wir weiter unten beim Mediaplayer schonmal ähnlich).

    Ich habe überhaupt keine Ahnung wo ich ansetzen kann (vielleicht habe ich auch keine von HTTP1.1 :-)

    Wenn du wirklich ein Formular für HTTP-Auth benutzen willst, läuft es darauf hinaus dass du die Daten auf dem Server abholst und an den User weiterreichst, am Userbrowser kannst du nichts verändern.
    Dazu machst du die HTTP-Verbindung auf, sendest die Anfrage mit Passwort, liest die Antwort bis zur ersten Leerzeile und  leitest den Rest an den User weiter.
    Wenn das auch über mehrere Seiten funktionieren soll, muss der Wrapper die Links in HTML-Dokumenten auch noch verwurschteln. Sowas ist im Ansatz im Archiv http://forum.de.selfhtml.org/archiv/2002/2/5425/#m30639, du musst allerdings noch den Teil der die Passwörter entgegennimmt (sofern erforderlich) schreiben.

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

    1. Moin Henryk!

      Zuerst mal herlichen Dank für die Lösungsvorschläge.

      Allerdings gibt dieser Code nur folgendes aus:
      HTTP/1.1 400 Bad Request

      Lass dir mal den weiteren Inhalt ausgeben da steht üblicherweise genauer drin was falsch ist.

      Bringt (mir) leider nichts:

      HTTP/1.1 400 Bad Request
      Date: Mon, 08 Apr 2002 13:56:35 GMT
      Server: Apache/1.3.12 (Unix) PHP/4.0.6 PHP/3.0.18 FrontPage/4.0.4.3
      Connection: close
      Content-Type: text/html; charset=iso-8859-1

      Drei Vermutungen:

      1. Du bist auf einem System gelandet dass zwei Bytes in \n drin hat und ein Dreibytezeilenendezeichen ist dem Webserver wohl zu bunt. Versuch mal \x0d\x0a als Zeilenendezeichen.

      \x0d\x0a an Stelle von \r\n bringt nichts. Allerdings kommt die gleiche Meldung wenn ich nur \x0a bzw. \n verwende, weshalb ich mal vermute, dass das nicht das Problem ist.

      1. HTTP/1.1 ist evt. nicht was du brauchst v.a. da der Server dann automatisch Keep-Alive machen will, versuch mal HTTP/1.0.

      Nein, HTTP/1.0 bringt nichts, ähnlicher Output mit dem Zusatz:
      Transfer-Encoding: chunked bei HTTP/1.1
      btw. auch bei einem HTTP/1.0-Request wird ein HTTP/1.1-Response geliefert.

      1. POST ist evt. nicht das was du willst (bin mir zwar nicht sicher, aber es könnte sein dass dann ein Body erwartet wird), versuch mal GET.

      Nein, leider auch Fehlanzeige.

      Irgendwas muss da prinzipiell falsch sein. Wenn ich die ganze Authorisierung weg lasse und es nur mit

      <?php
      $fp=fsockopen("www.domain.de",80);
      fwrite($fp, "GET /index.html HTTP/1.1\r\n");
      fwrite($fp, "Host: http://www.domain.de \r\n\r\n");
      $status=fgets($fp,200);
      fclose($fp);

      echo $status;
      ?>

      versuche, geht es schon nicht, vielleicht irgend wo ein " falsch gesetzt oder ein / an falscher Stelle, keine Ahnung!??

      Wenn man irgendwie den Status-Code herausbekommt, gibt es noch das Problem zum geschützten Verzeichnis mit bekanntem PW/Login weiterleiten. Aber wie mache ich das? Mit http://login:pw@www.domain.de/geschuetzt/ geht es nicht, da dies nicht RFC-konform ist (RFC 1738, Abschnitt 3.3: http://rfc.net/rfc1738.html). Wenn ich es ähnlich wie oben mache, merkt sich der Browser das vermutlich nicht, einfach in $PHP_AUTH_USER und $PHP_AUTH_PW schreiben bringt auch nichts, scheinbar übernimmt PHP nur die Daten vom Realm, nicht aber umgekehrt.

      Du kriegst die Username/Passwortdaten nicht in den Client ohne dass der User sie eingibt (hatten wir weiter unten beim Mediaplayer schonmal ähnlich).

      Das verrückte ist ja, dass es mit http://login:pw@www.domain.de/geschuetzt/ geht! Wenn ich damit eine Datei in einem geschützten Verzeichnis aufrufe und danach eine andere im selben Verzeichns oder einem Subverzeichnis (ohne login:pw@ in der URL) wird nicht nochmal nach PW und Login gefragt, auch wenn ich $PHP_AUTH_USER bzw. $PHP_AUTH_PW abfrage, sind die mit login:pw übergebenen Login- und PW-Angaben vorhanden (getestet mit IE 5.x, NN 4.7 und Mozilla 0.9.9)! D.h. das wäre eigentlich DIE Lösung, nur sie ist nicht RFC-konform :(

      Ich habe überhaupt keine Ahnung wo ich ansetzen kann (vielleicht habe ich auch keine von HTTP1.1 :-)

      Wenn du wirklich ein Formular für HTTP-Auth benutzen willst, läuft es darauf hinaus dass du die Daten auf dem Server abholst und an den User weiterreichst, am Userbrowser kannst du nichts verändern.
      Dazu machst du die HTTP-Verbindung auf, sendest die Anfrage mit Passwort, liest die Antwort bis zur ersten Leerzeile und  leitest den Rest an den User weiter.
      Wenn das auch über mehrere Seiten funktionieren soll, muss der Wrapper die Links in HTML-Dokumenten auch noch verwurschteln. Sowas ist im Ansatz im Archiv http://forum.de.selfhtml.org/archiv/2002/2/5425/#m30639, du musst allerdings noch den Teil der die Passwörter entgegennimmt (sofern erforderlich) schreiben.

      ... und um eingebundene Bilder, .pdf und sonstiges muss ich mich auch so kümmern, wenn ich das richtig verstehe. Das wollte ich aber gerade damit vermeiden, dass ich auf den bestehenden .htaccess-Schutz aufbaue, sonst wäre das ganze ja auch mit Daten ausserhalb des document roots zu lösen, die mit SessionID dynamisch erzeugt werden.

      Wenn man mal auf RFC usw. keine Rücksicht nimmt, geht es über die Schiene Login/PW an .htpasswd validieren (s. http://www.zend.com/zend/tut/authentication.php#Heading10), wenn die oben versuchte Methode über fsockopen wirklich nicht funktionieren sollte. Allerdings ist das etwas aufwendig, da man immer den path zur .htpasswd eintragen muss und der (lesende) Zugriff darauf nur auf dem eigenen Server möglich ist. Mit der obigen Variante könnte man so auch mal PW und Login auf enem anderen Server verifizieren. Aber wie gesagt, das wäre nur etwas aufwendiger, aber möglich und ohne Konflikte mit einem RFC. Größer ist das Problem der Login/PW-Daten. Da dies so prima mit http://login:pw@www.domain.de/geschuetzt/ funktioniert, bin ich fast versucht RFC RFC sein zu lassen. Hmm, wo könnte es dann Probleme geben?

      Clemens

      1. Moin,

        Lass dir mal den weiteren Inhalt ausgeben da steht üblicherweise genauer drin was falsch ist.

        Bringt (mir) leider nichts:

        HTTP/1.1 400 Bad Request
        Date: Mon, 08 Apr 2002 13:56:35 GMT
        Server: Apache/1.3.12 (Unix) PHP/4.0.6 PHP/3.0.18 FrontPage/4.0.4.3
        Connection: close
        Content-Type: text/html; charset=iso-8859-1

        Das sind nur die Header, die interessanten Sachen stehen im Body. Ich hab das eben mal lokal gemacht:
        <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
        <HTML><HEAD>
        <TITLE>400 Bad Request</TITLE>
        </HEAD><BODY>
        <H1>Bad Request</H1>
        Your browser sent a request that this server could not understand.<P>
        Client sent malformed Host header<P>
        <HR>
        <ADDRESS>Apache/1.3.19 Server at fuzzy.homeip.net Port 80</ADDRESS>
        </BODY></HTML>

        Da siehst du das Problem sofort und kannst es beheben (im Host:-Header hat das http:// nix zu suchen).

        \x0d\x0a an Stelle von \r\n bringt nichts. Allerdings kommt die gleiche Meldung wenn ich nur \x0a bzw. \n verwende, weshalb ich mal vermute, dass das nicht das Problem ist.

        Ok, nimm aber besser trotdem die von mir genannte Schreibweise, falls du doch mal auf ein komisches System kommst.

        Nein, HTTP/1.0 bringt nichts, ähnlicher Output mit dem Zusatz:
        Transfer-Encoding: chunked bei HTTP/1.1

        Ja, deswegen würde ich HTTP/1.0 nehmen wenn du nicht vorhast einigen zusätzlichen Quellcode mit den Standardeinstellungen von 1.1 zu beschäftigen: Keep-Alive (lässt sich leicht durch einen  Connection: Close  Header beim Request abstellen) und Chunked-Encoding (lässt sich gar nicht abstellen, höchstens Serverseitig).

        btw. auch bei einem HTTP/1.0-Request wird ein HTTP/1.1-Response geliefert.

        Das liegt irgendwo im Apache, eigentlich ist es nicht RFC-konform eine 1.1-Antwort auf eine 1.0-Anfrage zu senden, der Idianer macht das trotzdem. Weiss nicht ob es ein bekannter Bug ist, es ist jedenfalls im Archiv auch schonmal aufgefallen.

        Das verrückte ist ja, dass es mit http://login:pw@www.domain.de/geschuetzt/ geht! Wenn ich damit eine Datei in einem geschützten Verzeichnis aufrufe und danach eine andere im selben Verzeichns oder einem Subverzeichnis (ohne login:pw@ in der URL) wird nicht nochmal nach PW und Login gefragt, auch wenn ich $PHP_AUTH_USER bzw. $PHP_AUTH_PW abfrage, sind die mit login:pw übergebenen Login- und PW-Angaben vorhanden (getestet mit IE 5.x, NN 4.7 und Mozilla 0.9.9)! D.h. das wäre eigentlich DIE Lösung, nur sie ist nicht RFC-konform :(

        Ja, lass das lieber, es gibt keine richtige Lösung die diesem Muster folgt.

        ... und um eingebundene Bilder, .pdf und sonstiges muss ich mich auch so kümmern, wenn ich das richtig verstehe.

        Japp, daher mein Verweis auf das Skript im Archiv, das macht das im großen und ganzen ganz gut.

        Da dies so prima mit http://login:pw@www.domain.de/geschuetzt/ funktioniert, bin ich fast versucht RFC RFC sein zu lassen. Hmm, wo könnte es dann Probleme geben?

        In allen Browsern die sich an den RFC halten (bin erstaunt dass das der Mozilla nicht macht) fährst du gegen den Baum, ausserdem sind Passwort und Benutzername in der History.

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

        1. Hallo Henryk!

          Das sind nur die Header, die interessanten Sachen stehen im Body. Ich hab das eben mal lokal gemacht:
          <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
          <HTML><HEAD>
          <TITLE>400 Bad Request</TITLE>
          </HEAD><BODY>
          <H1>Bad Request</H1>
          Your browser sent a request that this server could not understand.<P>
          Client sent malformed Host header<P>
          <HR>
          <ADDRESS>Apache/1.3.19 Server at fuzzy.homeip.net Port 80</ADDRESS>
          </BODY></HTML>

          Da siehst du das Problem sofort und kannst es beheben (im Host:-Header hat das http:// nix zu suchen).

          Das darf doch nicht wahr sein oder ;-) Jetzt läuft es (mit GET und ohne http://) Heißen Dank!!

          Das verrückte ist ja, dass es mit http://login:pw@www.domain.de/geschuetzt/ geht! Wenn ich damit eine Datei in einem geschützten Verzeichnis aufrufe und danach eine andere im selben Verzeichns oder einem Subverzeichnis (ohne login:pw@ in der URL) wird nicht nochmal nach PW und Login gefragt, auch wenn ich $PHP_AUTH_USER bzw. $PHP_AUTH_PW abfrage, sind die mit login:pw übergebenen Login- und PW-Angaben vorhanden (getestet mit IE 5.x, NN 4.7 und Mozilla 0.9.9)! D.h. das wäre eigentlich DIE Lösung, nur sie ist nicht RFC-konform :(

          Ja, lass das lieber, es gibt keine richtige Lösung die diesem Muster folgt.

          Mhm, leider!

          ... und um eingebundene Bilder, .pdf und sonstiges muss ich mich auch so kümmern, wenn ich das richtig verstehe.

          Japp, daher mein Verweis auf das Skript im Archiv, das macht das im großen und ganzen ganz gut.

          Ja, Danke für die "grobe Lösungsskizze"!

          Da dies so prima mit http://login:pw@www.domain.de/geschuetzt/ funktioniert, bin ich fast versucht RFC RFC sein zu lassen. Hmm, wo könnte es dann Probleme geben?

          In allen Browsern die sich an den RFC halten (bin erstaunt dass das der Mozilla nicht macht) fährst du gegen den Baum, ausserdem sind Passwort und Benutzername in der History.

          Ja, die History, das ist wirklich ein weiterer gewichtiger Grund es zu lassen, muss nochmal überlegen, ob mir der Aufwand das wert ist, habe da doch das ein oder andere JS drinnen und auch Frames, das hört sich gewaltig nach Arbeit an :-/, und ist leider nicht so einfach, wie ich anfangs dachte.

          Nochmals vielen Dank für die Hilfe!!

          Clemens

        2. Hi,

          btw. auch bei einem HTTP/1.0-Request wird ein
          HTTP/1.1-Response geliefert.
          Das liegt irgendwo im Apache, eigentlich ist es
          nicht RFC-konform eine 1.1-Antwort auf eine 1.0-
          Anfrage zu senden, der Idianer macht das trotzdem.
          Weiss nicht ob es ein bekannter Bug ist, es ist
          jedenfalls im Archiv auch schonmal aufgefallen.

          hat schon mal jemand versucht, den Apache "zwangszuverdummen"?

          http://httpd.apache.org/docs/env.html#special

          Viele Grüße
                Michael

  2. Hi Clement,

    Ich habe überhaupt keine Ahnung wo ich ansetzen kann
    (vielleicht habe ich auch keine von HTTP1.1 :-)

    das Problem ist wirklich (wie von Henryk beschrieben), den Browser irgendwie dazu zu überreden, als Gedächtnis zu fungieren.

    Was Du tun könntest, wäre, extern (zwischen Browser und Deinem PHP-Skript) einen Cookie zu kommunizieren und diesen von Deinem PHP-Skript gegenüber dem eigentlichen Server-Request in einen Authenticate-Header umzusetzen.
    Denn der Cookie ist das einzige, was Du halbwegs zuverlässig in den Browser setzen kannst - die Authentication jedenfalls überhaupt nicht.

    Viele Grüße
          Michael