MoaByter: php: header(link) funktioniert auf dem Server nicht

Hallo, seit kurzem erst arbeite ich mit php (XAMPP), komme aus der C#-Ecke.

Ich erstellte auf einer Webseite Liste für unsere Treffen, möchte diese jetzt erweitern mit einer kleinen "Pinwand", wo die Mitglieder sich kurze Nachrichten schreiben können. Dazu gibt#s - klar - eine Anmeldeseite zur Passworteingabe. Im dortigen php-Script prüfe ich die Eingaben und das Script soll je nach Ergebnis weiterleiten auf die eigentliche Pinwand. Leider funktioniert das leider nicht, ich erhalte eine weiße, leere Seite. auf XAMPP läuft's einwandfrei, auf dem Server eben nicht.

if (pw_ein == pw_speicher)
{
  $sid = trim(substr($s_user, 0, 4), ' {}');
  #session_start();
  $_SESSION['user_id'] = $sid;
  $_SESSION['user_nm'] = $anm_nm;
  #include "pinwand_anm_html.php";
  header ('Location: https://www.lypo.eu/kltr160423/pinwand_html.php', false, 201);
  exit;
}

Wie man sieht, habe ich die Webseiten bisher über "include" integriert, aber das ist alles andere als okay. Das macht ein sinnvolles Navigieren unmöglich.

Bei strato sagte man mir, das Redirect sei gesperrt, es funktioniere nur ein dauerhaftes Redirect. Aber das will ich nicht erreichen, ich will ergebnisabhängig auf die richtige Seite weiterleiten. Was tun? Kann jemand helfen? Danke schonmal im Voraus.

MoaByter

Grmpf! Wie bekomme ich den Code richtig zur Anzeige?

akzeptierte Antworten

  1. Formatiert:

    if (pw_ein == pw_speicher) {
    	$sid = trim(substr($s_user, 0, 4), ' {}');
    	#session_start();
    	$_SESSION['user_id'] = $sid;
    	$_SESSION['user_nm'] = $anm_nm;
    	#include "pinwand_anm_html.php";
    	header ('https://www...html.php', false, 201);
    	exit;
    }
    

    funktioniert auf dem Server nicht

    Fehlermeldung? Eintrag im Error-Log?

    Bei strato sagte man mir, das Redirect sei gesperrt,

    Hä? Ok. Der Kundendienst war vorher beim Blödmarkt, dort, wo man kein Fachchinesisch kann oder in der DDR („sowas jibt es garnich!")

    Aber diese Zeile ist definitiv falsch:

    header ('Location: https://www...html.php', false, 201);
    

    muss sein:

    header ('Location: https://www...html.php',true, 301);
    

    oder

    http_response_code( 301 );
    header ( 'Location: https://www...html.php' );
    

    Womöglich braucht es ein

    <?php ob_start(); ?>
    

    um das Output-Buffering zu aktivieren.

  2. if (pw_ein == pw_speicher) {
    

    Du wirst bitte auf keinen Fall Passwörter im Klartext speichern, sondern Hashes.

    Sonst stehst Du am Montag in der Zeitung. Genau wie die, die sich besoffen auf dem Heimweg machten.

    1. wow, schnelle Antwort! Sei dir meines Dankes gewiss.

      Wenn du mir jetzt noch verrätst, wie du den Code formatiert hast, machst du mich zu einem glücklichen Menschen. 🤣 Mit dem </>-Zeichen funktioniert's nicht.

      201 oder 301: Jaaa, ich dachte, es sei keine aduerhafte Weiterleitung (301), sondern eher eine Neu-Kreation (201), oder liege ich mit den Begriffen falsche?

      Jetzt zu den Fakten: Keine Fehlermeldung, nur weiße, inhaltslose Seite, wo das Error-Log ist, weiß ich nicht. Ich habe jedenfalls nichts diesbezügliches angelegt. Dummerweise funktioniert's auch ohne 301/201 nicht, habe ja alles Mögliche schon ausprobiert. Wenn ich das Ding ganz an den Anfang setze, geht's:

      <?php
         header(...)
      

      Nur da nützt es mir nix.

      Das "ob_start()" hatte ich schon mal auf php.net gesehen, konnte damit nix anfangen, dachte, es sei für besondere Fälle.

      Passwörter: Interssiert keine, das ist alles noch Testphase, die PWs sind "raupe", "anne" und so schwierige Wörter. alles nur Test.

      Mit "Sicherheit" habe ich mich vermutlich noch nicht ausreichend befasst, schütze das vor Hackereingaben (diese Seite ist tief vergraben auf dem Server) lediglich mit

      htmlspecialchars($_POST);
      

      Ich hätte in meinem Bespiel schreiben müssen:

      if ($pw_ein == $pw_gespeichert)
      

      klar...

      Also werde ich mich mit ob_start(); befassen. Interssanterweise hatte meine "header(...)"-Lösung heute mal ganz kurzfristig funktioniert, weiß der Geier, warum. 5 Minuten später war der Spuk vorbei. Grmpf!

      Danke! Ich habe noch sooo viele Fragen, komme bestimmt bald wieder. 🙃

      1. Jetzt zu den Fakten: Keine Fehlermeldung, nur weiße, inhaltslose Seite, wo das Error-Log ist, weiß ich nicht.

        Um diesen Punkt - wo das Error-Log ist und wie Du (temporär) Fehler anzeigen kannst - musst Du Dich zuerst kümmern.

        Außerdem wären da noch die Entwicklungstools Deines Browsers. Da kannst Du wenigstens den Statuscode sehen. Und wenn Du schon dabei bist: Eine „inhaltslose Seite“ sieht man auch, wenn im HTML „ausreichend gründliche“ Fehler sind. Im Quelltext sieht man dann womöglich mehr…

      2. 201 oder 301: Jaaa, ich dachte, es sei keine aduerhafte Weiterleitung (301), sondern eher eine Neu-Kreation (201), oder liege ich mit den Begriffen falsche?

        Nun, es ist keine Neukreation. Das wäre der Fall, wenn durch einen Upload eine völlig neue Ressource für den Abruf entstanden wäre. Ist es aber nicht.

        Das "ob_start()" hatte ich schon mal auf php.net gesehen, konnte damit nix anfangen, dachte, es sei für besondere Fälle.

        Das ist es auch. Bei modernen PHP-Versionen ist das Output-Buffering (ob_) nämlich aut of the Box angeschaltet, man kann es aber abschalten (Weiter unten steht beschrieben, was dann genau in Deinem Fall passiert) oder den Inhalt vorzeitig senden. Mit headers_send() kann man nur prüfen, ob das Kind bereits ins Wasser gefallen ist, mit ob_start() einen kleinen Damm errichten, der das Wasser (Senden des Contents und also der Header-Zeilen) bis zu einem ob_flush() oder dergleichen aufhält. Damit das funktioniert muss dann

        <?php
        ob_start();
        

        ganz am Anfang des Skriptes stehen. Nichts davor: kein Text, kein HTML, keine Byte Order Mark!

        Wenn ich das Ding ganz an den Anfang setze, geht's:

        <?php
          header(...)
        

        Nur da nützt es mir nix.

        Genau das lässt mich vermuten, dass da schon „was“ gesendet wurde. Und wenn die header schon via Webserver gen Browser gesendet wurden, ist das Kind im Wasser und abgetrieben, Du kannst ihm keine Badesachen hinterherwerfen. Dessen Hilferufe hörst Du nicht, weil Du ANC-Kopfhörer auf hast.

        1. Langsam kloppen sich die Teile um die erste Stelle, denn "session_start()" muss ja auch ganz am Anfang stehen.

          Ich sollte wohl mal meinen Aufbau darlegen: Von einer Webseite aus gelangt man mit 'nem Klick auf die 'pinnwand.php'. Diese enthält nur php-Code, sie hat verschiedene Aufgaben. Erst werden die $_GETs und $_POSTs in Ass.Arrays umgewandelt (mit htmlspecialchars($_POST[xy]) z.B., die Strings werden in base64 encodiert um Zeilenumbrüche u.a. zu schützen, an die Passwort-Spezialität muss ich noch ran), dann werden verschiedene Datei eingelesen, die ich zur Weiterverarbeitung benötige (alles Kilobyte-Dateien), und dann folgen die Aufgaben.:

          1. ohne POST-Übergabe wird das Anmelde-HTML eingelesen (mit exit),
          2. Dateiupload für Profil-Erstellen mit Avatar (kein exit, die Datenverabeitung folgt),
          3. Das Einlesen der übergebenen Profil-Daten der Nutzer (mit exit),
          4. Das Speichern der übergeben Daten der Beiträge der Nutzer, mit exit, denn dann ist Schluss.

          Von 3. und 4. aus sollte eigentlich die Seite mit den Beiträgen der Nutzer aufgerufen werden, was leider nur bei XAMPP funktioniert, auf dem Strato-Server eben nicht. Der HTML-Code ist einwanfrei, das kann ich ganz gut, zur Hilfe habe ich das HTML-Prüftool von Mozilla und für die Netzwerkkommunikationsanalyse das "HTTP-Header-Live"-Tool.

          Was PHP so tut und treibt, überprüfe ich mit "var_dump()" und "echo", das funktioniert eigentlich ganz gut. So stehen am Anfang der HTML-Seite dann die Ergebnisse der Prüfungen.

          Wie gesagt: Ich bin neu auf dem Gebiet, und meine Entwicklung mag nicht perfekt sein, aber alles in Allem funktioniert's gut - bis eben auf ... die "header()"-Geschichte. Ich habe es jetzt erstmal provuisorisch gelöst, indem ich auf der Anmeldeseite nach der Datenverarbeitung eine Meldung mit einem "Weiter"-Button ausgebe, den die Nutzer anklicken müssen. Tatsächlich funktioniert die Nutzerweiterleitung, als das, was vom Nutzer-Computer kommt, einwandfrei, nur die PHP-interne nicht. Ich habe es mal über einen PHP-gesteuerten meta-Eintrag probiert, funktioniert, dauert Äonen! Naja, 20-30 Sekunden.

          Wenn der Nutzer also mit seinem Klick die "pinwand.php" aufruft, ist nur der Request an den Server gegangen, gesendet wird in dieser datei nicht, sondern nur Variablen gefüllt, Daten gelesen, überprüft, $_SESSION gefüllt usw. Wenn das alles durch ist, kommt erst der über include eingefügte Webseitentext der Anmeldeseite.

          1. Hallo MoaByter,

            Erst werden die $_GETs und $_POSTs in Ass.Arrays umgewandelt

            Das musst Du nicht tun. $_GET und $_POST sind bereits assozative Arrays, mit denen kannst Du ganz normal arbeiten.

            Eine Übertragung von $_GET/$_POST Daten in eine eigene Datenstruktur kann natürlich trotzdem sinnvoll sein, wenn man aus einem geposteten Formularinhalt ein fachliches Objekt erzeugt und damit dann weiterarbeitet.

            (mit htmlspecialchars($_POST[xy]) z.B., die Strings werden in base64 encodiert um Zeilenumbrüche u.a. zu schützen

            Das sollst Du nicht tun. Die vom User erhaltenen Eingaben bleiben im Normalfall so, wie sie sind, und werden nur dann maskiert und behandelt, wenn es nötig ist. Also wenn sie in ein SQL Statement eingeflickt werden müssen oder wenn sie zum Browser zurückgeschickt werden. Vorher nicht. Wenn dein Benutzer Dir "Hallo <welt>" eingibt, dann möchtest Du das auch so speichern und verarbeiten, und nicht während der V Phase der EVA mit &lt und &gt herumfummmeln.

            Eine base64-Codierung von Benutzereingaben zum „Schutz von Zeilenumbrüchen“ klingt nach einer schrägen Merkwürdigkeit oder einem schweren Missverständnis.

            Langsam kloppen sich die Teile um die erste Stelle

            session_start gehört nach vorne, ja.

            Aber ob_start? Den braucht man seltener, als man denkt. Eine Verarbeitung nach EVA Prinzip besagt ja, dass Du alles, was Du zum Erstellen der Ausgabe brauchst, erstmal im Arbeitsspeicher zusammensuchst und dann zum Schluss an Hand der gesammelten Daten die Seite raushaust.

            Aber das ist nicht immer effizient. Gerade bei großen HTML Seiten entsteht so eine ordentliche Latenz, weil der Server erstmal vor sich hin werkelt und den Browser warten lässt.

            Wenn Du einen Webrequest verarbeitest, bist Du erstmal in eine "Findungsphase", wo Du rauskriegen musst, was genau zu tun ist. Musst Du das Form mit Fehlermeldung zurückweisen, musst Du die Seite im Format A, B oder C ausgeben, musst Du erstmal einen Login verlangen, etc.

            Aber sobald das klar ist und nun "nur noch" Daten rauszuhauen sind, kannst Du auch damit anfangen. Wenn Du 100 Zeilen aus einer DB liest und als HTML Table ausgeben willst, dann ist es schlecht, das so zu tun:

            • 100 Zeilen in ein Array lesen
            • Aus 100 Zeilen HTML bauen und mit ob_start buffern
            • Den Buffer rausrotzen

            Wenn Du vor der Table Ausgaben machen willst, die Du erst erzeugen kannst wenn alle 100 DB Zeilen gelesen sind, OKAY, dann muss das. Aber auch dann kannst Du das gebaute HTML vermutlich sofort ausgeben und musst es nicht puffern. Das hat nämlich den Vorteil (sofern der Webserver nicht aggressiv puffert), dass der Browser das HTML vom Tabellenanfang schon vorliegen hat, während Du noch den Rest der Tabelle generierst. Das Parsen des HTML und das Erzeugen des HTML überlappen sich damit zeitlich, und für den Benutzer verringert sich die Wartezeit. Und Du brauchst weniger Serverspeicher, was auf einem unter Dampf stehenden Webserver immer eine gute Sache ist.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. $_GET, $_POST werden nur zur Sicherheit umgewandelt, also wenn jemand versucht, mit einem selbstgebastelten Request den Server zu schrägen Sachen zu verleiten. Die Nachteile sind bekannt, spitze Klammern haben in einem Namen-/Adressfeld jedoch nix zu suchen. Ich hab's mit dem javascript-Befehl location.href = "..." verucht: Sollte nicht funktioniert, tuts aber.

              Was darf ich unter einem "fachlichen Object" verstehen? C# ist sehr stark Object-orientiert, PHP nicht so sehr. Die Arbeiten in diesen beiden Sprachen unterscheiden sich doch sehr, ich bin immer versucht "string $s_xy = ..." oder ähnliches zu schreiben.

              Ohne base64-Encodierung bekomme ich z.B. auch die in den Beiträgen verwendeten Smileys nicht rein. 😊 Und u.a. spitze Klammern, werden eben "entschärft".

              "ob_start" war ja angedacht, um mein "header(...)"-Problem zu lösen, denn das war ja das eigentliche Thema der Anfrage. Der Server folgt dem im Header angegeben Link nicht, sondern ruft die eigene Seite wieder auf, wo ich wieder vor dem glaichen Problem stehe. Und in der Adresszeile sollte ja auch die richtige Datei angegeben sein und nicht die Adresse der Script-Datei. Wenn ich die Login-Prüfung aber in die richtige, die Ziel-Datei verlege und es sich dort herausstellt, dass der Login falsch war, muss ich wieder die Anmeldeseite aufrufen, was eben wieder nicht funktioniert. Es ist zum Mäusemelken! Grmpf!

              Es gab ja wohl die Vermutung, dass der Server schon vorab etwas versendet hat, sodass die Adresse nicht mehr zu ändern ist. Das ist aber nicht der Fall. Gesendet wird erst, wenn alle Daten vorliegen, und lange dauert das auch nicht, die Datenmengen sind eher klein: 250 bis 300 Kilobyte für die gesamte Seite einschließlich der Grafiken, CSS und JS.

              Ich rätsele halt immer noch daran rum, warum es im XAMPP einwandfrei funktioniert und ich vom Server nur eine leere Seite erhalte mit der "alten", also der Script-Adresse. Okay, die Zieldatei enthält im Kopf auch etwas PHP, das dient dazu, bei Direktaufruf der Seite zu prüfen, ob der Nutzer eingeloggt ist, dann erhält er erweiterte Inhalte, kann seine Inhalte editieren, sein Profil aufrufen - sowas eben. Einen Screenshot der Zielseite habe ich mal angehängt.

              Speicherplatz und Prozessorzeit zu sparen sind für mich ein Grundprinzip in der Programmierung.

              1. Hallo MoaByter,

                $_GET, $_POST werden nur zur Sicherheit umgewandelt, also wenn jemand versucht, mit einem selbstgebastelten Request den Server zu schrägen Sachen zu verleiten. Die Nachteile sind bekannt, spitze Klammern haben in einem Namen-/Adressfeld jedoch nix zu suchen. I

                Kann man so sehen, aber wenn der Kontextwechsel korrekt durchgeführt wird (also die passende Escape-/Quote Funktion beim DB Zugriff und ein htmlspecialchars bei der Ausgabe auf den Browser), dann kann man basteln wie man will und es passiert nichts.

                Ohne base64-Encodierung bekomme ich z.B. auch die in den Beiträgen verwendeten Smileys nicht rein.

                Dann hast Du ein Encoding-Problem. Setze konsequent auf Unicode, bei

                • der Ausgabe von Text an den Browser
                • beim Speichern deiner PHP Sourcen und der Textdateien, die Du an den Browser schickst (CSS und JS)
                • beim Herstellen der DB-Verbindung
                • beim Speichern der Daten in der DB

                Bei der DB musst Du darauf achten, utf8mb4 zu verwenden. Die ursprüngliche UTF8-Unterstützung von MySQL konnte nur UTF-Sequenzen aus 3 Zeichen verarbeiten, das reicht nur für die BMP (Basic Multilingual Plane, Zeichen \u0000 bis \uffff).

                Was darf ich unter einem "fachlichen Object" verstehen? C# ist sehr stark Object-orientiert, PHP nicht so sehr.

                Ja, in PHP kann man prozedural, hybrid oder auch massiv objektorientiert arbeiten. Du kannst bspw. eine Factory-Klasse erstellen, die sich $_GET/$_POST anschaut und das passende Controller-Objekt zurückliefert, um den Request zu verarbeiten.

                // demo.php
                require "autoloader.php";
                
                $router = new RequestRouter("demo");
                $controller = $router->getController($_GET, $_POST);
                
                $controller->run($_GET, $_POST);
                

                Der RequestRouter braucht natürlich Steuerinformationen, so dass er aus dem Namen der Seite, $_GET und $_POST ermitteln kann, welche Controllerklasse den Request verarbeiten kann. Er kann auch einen InvalidRouteController zurückliefern, wenn er keinen passenden Controller findet.

                Die run Methode im Controller kann dann aus den Requestparametern die passende Aktion bestimmen und durchführen. Geht alles, muss man nur bauen (oder mit Frameworks wie Symphony fertig einbinden). Braucht natürlich dann mehr Speicher und Laufzeit - das ist normal.

                Warum es bei dir auf dem Servern nicht funktioniert, kann man ohne Error Log nicht sagen.

                Wenn Du keine PHP Fehler-Logdatei auf dem Server hast oder kennst, dann könntest Du mal eine Miniseite hochladen, die phpinfo() ausführt. Schau, was unter error_log konfiguriert ist. Oder frag deinen Hoster-Support, wohin PHP Errors loggt.

                Wichtig ist natürlich, dass Errors überhaupt ausgegeben werden, dafür brauchst Du die richtige error_reporting Einstellung.

                Wenn PHP irgendwelche Fehlermeldungen ausgibt, und sei es auch nur ein Notify, bevor Du die Header setzen willst, dann ist es zu spät. Die Header sind geschrieben und können nicht mehr ergänzt werden.

                In meinen (unprofessionellen) Programmen integriere ich deshalb zumeist mit set_error_handler, set_exception_handler und register_shutdown_function ein eigenes Errorhandling, die alle Meldungen, die PHP schreibt, in ein Array schreibt statt sie zum Browser zu schicken.

                Am Ende der Seite oder im Shutdown gebe ich dieses Array dann als Liste aus. Auf diese Weise kann ich die Meldungen bändigen und verhindere, dass sie mitten ins HTML gerotzt werden.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hallo Rolf B., die Sache mit dem Kontext(-Wechsel) ist mir noch nicht klar, das muss ich noch weiter erforschen. Escape-/Quote? Hm... Bedenke: Ich bin Neuling. Bei C# gibt's sowas meines Wissens nach nicht oder ich hab's nicht gebraucht.

                  Noch speichere ich die Texte und alles andere in ganz normalen Textdateien, darin habe ich Übung und in C# ist das die einfachste, schnellste Lösung. Da zeigt sich jetzt auch, warum Zeilenumrüche ein Problem darstellen. Ich habe mir gesagt, wenn es auf die Art gut läuft, läuft's auch sonst gut - und eigentlich tut's das ja auch. Das stellt ja auch kein Problem dar, verbessert oder verschlimmert nichts an der Webseite, war mir nur sicherer. Ich 'nehme sie raus, nur was passiert, wenn jemand sich 'nen Request bastelt, der im $_POST z.b. <?php gefährlicher_Code; ?> 'reinschreibt?

                  Meine Texte schreibe ich alle in Notepadd++, das ist fest auf UTF-8 eingestellt, Unicode geht da gar nicht. Auch in den Formularen ist accept-charset="utf-8" eingestellt, daran kann's also nicht liegen. Ob es mit den Smileys auch ohne Base64 läuft, müsste ich nochmal testen, vielleicht war ein anderer Fehler schuld.

                  "fachliche Objekte" werde ich wohl erst einsetzen, wenn ich genau weis, was dahinter steckt, dein Beispiel hilft mir da noch(!) wenig, vermutlich später. 🙃

                  Tja, das error_log...! Ich hatte früher mal phpinfo() gestartet, weil ich die PHP-Version wissen wollte (is' 8.0.27, XAMPP läuft mit 8.2) und mich mit den Server-Einstellungen befasst hatte - sind zuviele. Aber den Eintrag "error_log" habe ich gefunden - Scrennshot hängt an.

                  if ($treffer)// Nutzername/Passwort ok.
                  {
                  	ob_start();
                  	$sid = trim(substr($s_user, 0, 4), ' {}');
                  	session_start();
                  	$_SESSION['user_id'] = $sid;
                  	$_SESSION['user_nm'] = trim($treffer[1]);
                  	$insert = "gnDas Einloggen war erfolgreich!";
                  	#header('Location: pinwand_html.php');
                  	ob_end_flush();
                  	include "pinwand_anm_html.php";
                  	exit;
                  }
                  

                  Wie schon erwähnt: Nach der Benutzernamen/Passwortprüfung (ja, mit password_verify) sollte die Seite "pinwand_html,php" aufgerufen werden, aber das tut er nicht. Das exit(); steht jetzt nur für das include... da, damit er nicht mehr den Rest durchläuft. Ohne das exit funktioniert's auch nicht, er verlässt die Seite einfach nicht, ignoriert das Setzen des Headers. (Ob ich das mit dem ob_start(); und ob_end_flush(); richtig gemacht habe, weiß ich nicht. Und sei gewiss, da ist davor nix, was er ausgeben könnte.

                  Okay, zur Prüfung habe ich also die $_GET- und $_POST- Bearbeitung 'rausgenommen, damit auch die base64_encode(htmlspecialchars(); ... öhm ...

                  Überraschung:

                  ...tja, funktioniert auch auf dem Server. Jetzt komme ich nun doch schwer ins Grübeln. Die Frage bleibt aber offen, warum sich der XAMPP nicht daran stört, was den strato-Server ins Stolpern bringt? Und: Was ist an dem PHP davor, was das Senden von Daten aktivierte) Hier mal der bisherige Code davor:

                  <?php
                  /*array*/	$aa_post = array(); $aa_get = array(); $aa_files = array(); $aa_muster = array(); $a_user = array();
                  /*string*/	$anm_nm = "default"; $insert = ""; $post_id;
                  /*int*/		$anm_nm_id = 0;
                  /*bool*/		$b_post = !empty($_POST); $b_get = !empty($_GET); $b_files = !empty($_FILES); $b_tm = "false";
                  
                  ### $_GET-Übergabe einlesen, schützen
                  	if (!$b_get)
                  	{
                  		foreach ($_GET as $key => $val)
                  			$aa_get[htmlspecialchars($key)] = htmlspecialchars($val);
                  	}
                  	
                  ### bei leerer Übergabe sofort zur Anmeldung
                  	if (!$b_post)
                  	{
                  		include "pinwand_anm_html.php";
                  		exit;
                  	}
                  
                  ###  $_POST-Übergabe einlesen, schützen
                  	if ($b_post)
                  	{
                  		$send = htmlspecialchars($_POST["send"]);
                  		if ($send == "Anmelden" || $send == "Anlegen" || $send == "Speichern")
                  			foreach ($_POST as $key => $val)
                  			{
                  				$key = htmlspecialchars($key);
                  				if (str_starts_with($key, 'b64_')) $val = base64_encode(htmlspecialchars($val));
                  				$aa_post[$key] = $val;
                  				#if (str_starts_with($key, 'hash_')) $aa_post['hash'] = password_hash($val, PASSWORD_DEFAULT);
                  			}
                  		else exit;
                  		$anm_nm_id = $aa_post["nutz_id"]?? 0;
                  	}
                  	
                  ### Muster-Datei für verschiedene Zwecke aufrufen
                  	$aa_muster = unserialize(file_get_contents('muster_nb_assoc_php'));
                  	$text_id_neu = (int) $aa_muster['int_beiträge_max'] + 1;
                  	$user_id_neu = (int) $aa_muster['int_nutzer_max'] + 1;
                  	
                  ### Nutzerdatei laden
                  	$s_f_nutzer = trim(file_get_contents("nutzer"));
                  	$insert = "";
                  	$a_user = explode("}\r\n\r\n{", $s_f_nutzer);
                  
                  # hier kommt die Namen/Passwort-Prüfung s.o.
                  

                  So hat den deine Ermahnung - andere gaben sie ja auch schon - sehr geholfen. Jedenfalls hab ich mächtig dazu gelernt und erfahren, dass PHP doch schwieriger ist, als ich dachte. Ich bin da recht blauäugig 'rangegangen: "Was andere können, kann ich auch". Bei C# hat's geklappt, hier wird's das schlussendlich auch.

                  error_log_strato

                  1. Hallo,

                    anscheinend fehlt dir noch eine Menge Grundwissen.

                    Meine Texte schreibe ich alle in Notepadd++, das ist fest auf UTF-8 eingestellt, Unicode geht da gar nicht.

                    UTF-8 ist eine gängige Form, Unicode zu schreiben. Unicode ist der im Web-Umfeld übliche Zeichensatz; UTF-8 ist eine Möglichkeit, wie Unicode in Bits und Bytes abgebildet wird.

                    if ($treffer)// Nutzername/Passwort ok.
                    {
                    	ob_start();
                    	$sid = trim(substr($s_user, 0, 4), ' {}');
                    	session_start();
                    	$_SESSION['user_id'] = $sid;
                    	$_SESSION['user_nm'] = trim($treffer[1]);
                    	$insert = "gnDas Einloggen war erfolgreich!";
                    	#header('Location: pinwand_html.php');
                    	ob_end_flush();
                    	include "pinwand_anm_html.php";
                    	exit;
                    }
                    

                    Da ist zuviel magic drin, als dass man versehen könnte, was du da wirklich tust. Also Code, bei dem man nicht weiß, was er bedeuten soll, woher die Daten kommen.

                    Wie schon erwähnt: Nach der Benutzernamen/Passwortprüfung (ja, mit password_verify) sollte die Seite "pinwand_html,php" aufgerufen werden, aber das tut er nicht.

                    Wer ist "er", und was tut die Funktion password_verify()? Normalerweise solltest du nicht mit Passwörtern im Klartext hantieren müssen.

                    Das exit(); steht jetzt nur für das include... da, damit er nicht mehr den Rest durchläuft. Ohne das exit funktioniert's auch nicht, er verlässt die Seite einfach nicht, ignoriert das Setzen des Headers.

                    Wer ist der geheimnisvolle er?

                    Der Computer kann's nicht sein. Computer sind grundsätzlich weiblich: Geheimnisvoll, undurchschaubar, manchmal auch ein bisschen zickig - aber man möchte doch nicht auf sie verzichten.

                    Dein Code ist völlig kryptisch und nicht nachvollziehbar. Vielleicht solltest du erstmal in Prosa beschreiben, was er eigentlich tun soll.

                    Einen schönen Tag noch
                     Martin

                    --
                    Kaffee ist nur schädlich, wenn Ihnen ein ganzer Sack aus dem 5. Stock auf den Kopf fällt.
                    1. Hi,

                      Der Computer kann's nicht sein. Computer sind grundsätzlich weiblich: Geheimnisvoll, undurchschaubar, manchmal auch ein bisschen zickig - aber man möchte doch nicht auf sie verzichten.

                      Computer können nicht weiblich sein. Denn sie tun genau das, was man ihnen sagt.

                      cu,
                      Andreas a/k/a MudGuard

                      1. Hallo MudGuard,

                        dieser Beitrag wurde gemeldet. Er ist unkonstruktiv.

                        Dem widerspreche ich. Unkonstruktiv ist das falsche Wort. Es ist sexistischer Mist aus dem letzten Jahrtausend, genau wie Martins Spruch im Posting davor (du hast im Mod-Kommentar drum gebettelt, Martin!).

                        Wir sind hier zwar zumeist alte weiße Männer, aber das müssen wir doch nicht dermaßen raushängen lassen, oder?

                        (Computer) tun genau das, was man ihnen sagt

                        Und das ist zumeist nicht das, was man von ihnen will.

                        Rolf

                        --
                        sumpsi - posui - obstruxi
                  2. Moin,

                    die Sache mit dem Kontext(-Wechsel) ist mir noch nicht klar, das muss ich noch weiter erforschen. Escape-/Quote? Hm... Bedenke: Ich bin Neuling. Bei C# gibt's sowas meines Wissens nach nicht oder ich hab's nicht gebraucht.

                    Kontextwechsel gibt es potenziell überall:

                    • Ausgabe nach HTML
                    • Speichern von Texten in einer CSV-Datei
                    • Zusammenbauen von SQL-Statements

                    Als Neuling ist es besonders wichtig, das direkt zu lernen, weil das einem viel Ärger ersparen kann.

                    Noch speichere ich die Texte und alles andere in ganz normalen Textdateien, darin habe ich Übung und in C# ist das die einfachste, schnellste Lösung. Da zeigt sich jetzt auch, warum Zeilenumrüche ein Problem darstellen.

                    Wenn Zeilenumbrüche ein Problem darstellen, dann musst du den Kontextwechsel in dein zeilenbasiertes Format korrekt behandeln.

                    Ich 'nehme sie raus, nur was passiert, wenn jemand sich 'nen Request bastelt, der im $_POST z.b. <?php gefährlicher_Code; ?> 'reinschreibt?

                    Das kommt darauf an, wo dieser String landet – in welchem Kontext.

                    Meine Texte schreibe ich alle in Notepadd++, das ist fest auf UTF-8 eingestellt, Unicode geht da gar nicht.

                    UTF-8 ist Unicode und Notepad++ hat so ein schönes Menü genannt Codierung und da finde zumindest ich einiges an Unicode-Codierungen 😉

                    Tja, das error_log...! Ich hatte früher mal phpinfo() gestartet, weil ich die PHP-Version wissen wollte (is' 8.0.27, XAMPP läuft mit 8.2) und mich mit den Server-Einstellungen befasst hatte - sind zuviele. Aber den Eintrag "error_log" habe ich gefunden - Scrennshot hängt an.

                    Ansonsten hilft dir natürlich auch das Error Reporting von PHP weiter.

                    if ($treffer)// Nutzername/Passwort ok.
                    {
                    	ob_start();
                    	$sid = trim(substr($s_user, 0, 4), ' {}');
                    	session_start();
                    	$_SESSION['user_id'] = $sid;
                    	$_SESSION['user_nm'] = trim($treffer[1]);
                    	$insert = "gnDas Einloggen war erfolgreich!";
                    	#header('Location: pinwand_html.php');
                    	ob_end_flush();
                    	include "pinwand_anm_html.php";
                    	exit;
                    }
                    

                    Wie schon erwähnt: Nach der Benutzernamen/Passwortprüfung (ja, mit password_verify) sollte die Seite "pinwand_html,php" aufgerufen werden, aber das tut er nicht. Das exit(); steht jetzt nur für das include... da, damit er nicht mehr den Rest durchläuft. Ohne das exit funktioniert's auch nicht, er verlässt die Seite einfach nicht, ignoriert das Setzen des Headers.

                    Kann es sein, dass der ganze Zweig gar nicht ausgeführt wird, weil die Bedingung nicht erfüllt ist?

                    (Ob ich das mit dem ob_start(); und ob_end_flush(); richtig gemacht habe, weiß ich nicht.

                    Die Frage kannst du mit Hilfe des PHP-Handbuch Kapitels zum Output Buffering eventuell selbst beantworten.

                    <?php
                    /*array*/	$aa_post = array(); $aa_get = array(); $aa_files = array(); $aa_muster = array(); $a_user = array();
                    /*string*/	$anm_nm = "default"; $insert = ""; $post_id;
                    /*int*/		$anm_nm_id = 0;
                    /*bool*/		$b_post = !empty($_POST); $b_get = !empty($_GET); $b_files = !empty($_FILES); $b_tm = "false";
                    
                    ### $_GET-Übergabe einlesen, schützen
                    	if (!$b_get)
                    	{
                    		foreach ($_GET as $key => $val)
                    			$aa_get[htmlspecialchars($key)] = htmlspecialchars($val);
                    	}
                    

                    Der Zweig hier ↑ wird nur ausgeführt, wenn $_GET leer ist, aber dann hat foreach nichts zu tun und $aa_get bleibt auch leer.

                    Unabhängig davon: Sofern du die Schlüssel und Werte nicht ausschließlich im HTML-Kontext ausgibst, kannst du dir das htmlspecialchars an der Stelle sparen.

                    ### bei leerer Übergabe sofort zur Anmeldung
                    	if (!$b_post)
                    	{
                    		include "pinwand_anm_html.php";
                    		exit;
                    	}
                    

                    Hier ↑ scheint die Bedingung korrekt.

                    ###  $_POST-Übergabe einlesen, schützen
                    	if ($b_post)
                    	{
                    		$send = htmlspecialchars($_POST["send"]);
                    		if ($send == "Anmelden" || $send == "Anlegen" || $send == "Speichern")
                    			foreach ($_POST as $key => $val)
                    			{
                    				$key = htmlspecialchars($key);
                    				if (str_starts_with($key, 'b64_')) $val = base64_encode(htmlspecialchars($val));
                    				$aa_post[$key] = $val;
                    				#if (str_starts_with($key, 'hash_')) $aa_post['hash'] = password_hash($val, PASSWORD_DEFAULT);
                    			}
                    		else exit;
                    

                    Du kannst formal auf die geschweiften Klammern beim if verzichten, aus Gründen der Nachvollziehbarkeit ist das aber wenig empfehlenswert.

                    $send braucht hier nicht mit htmlspecialchars behandelt zu werden, da du es ja nur gegen konstante Strings vergleichst. Und da möchtest du gerne mit === vergleichen, also auf inhaltliche und Typgleichheit. Allerdings fehlt dir noch die Prüfung, ob $_POST überhaupt einen Schlüssel send enthält.

                    Für Base64-Encodierung brauchst du vorher auch kein htmlspecialchars, denn nach Base64 bleiben nur 64 Zeichen übrig und keines davon ist im HTML-Kontext gefährlich.

                    So hat den deine Ermahnung - andere gaben sie ja auch schon - sehr geholfen. Jedenfalls hab ich mächtig dazu gelernt und erfahren, dass PHP doch schwieriger ist, als ich dachte. Ich bin da recht blauäugig 'rangegangen: "Was andere können, kann ich auch". Bei C# hat's geklappt, hier wird's das schlussendlich auch.

                    In C# musst du dich aber doch auch mit deiner Umgebung und den Dokumentationen auseinandersetzen.

                    Viele Grüße
                    Robert

              2. Moin,

                $_GET, $_POST werden nur zur Sicherheit umgewandelt, also wenn jemand versucht, mit einem selbstgebastelten Request den Server zu schrägen Sachen zu verleiten.

                • Welche Form der „Umwandlung“ findet denn dort statt?
                • Was hindert dich daran fehlerhafte Eingaben mit einem Fehler zu quittieren?

                Die Nachteile sind bekannt, spitze Klammern haben in einem Namen-/Adressfeld jedoch nix zu suchen.

                Kannst du mit Sicherheit sagen, dass es nicht irgend eine Sprache oder irgend ein Land gibt, in dem das doch erlaubt ist? Bis ich in Baden-Württemberg wohnte, dachte ich auch, dass z.B. Hausnummern eine einfache Sache sein …

                Ich hab's mit dem javascript-Befehl location.href = "..." verucht: Sollte nicht funktioniert, tuts aber.

                Was genau?

                Ohne base64-Encodierung bekomme ich z.B. auch die in den Beiträgen verwendeten Smileys nicht rein. 😊

                Das heißt, dass du beim Speichern Base64-kodierst und beim Lesen enkodierst? Dann hat deine Persistenz ein Zeichensatz-Problem, das du damit umgehst.

                Und u.a. spitze Klammern, werden eben "entschärft".

                kontext-gerecht maskiert

                "ob_start" war ja angedacht, um mein "header(...)"-Problem zu lösen, denn das war ja das eigentliche Thema der Anfrage. Der Server folgt dem im Header angegeben Link nicht, sondern ruft die eigene Seite wieder auf, wo ich wieder vor dem glaichen Problem stehe. Und in der Adresszeile sollte ja auch die richtige Datei angegeben sein und nicht die Adresse der Script-Datei. Wenn ich die Login-Prüfung aber in die richtige, die Ziel-Datei verlege und es sich dort herausstellt, dass der Login falsch war, muss ich wieder die Anmeldeseite aufrufen, was eben wieder nicht funktioniert. Es ist zum Mäusemelken! Grmpf!

                Hast du dir denn schon einmal mit wget, curl oder dem Netzwerk-Tab der Browser-Entwickler-Tools angeschaut, was deine Seiten jeweils an HTTP-Headern und Status zurückliefern? Und gibt es vielleicht einen Hinweis im error.log des Apachen oder wenn du das Log-Level in PHP erhöhst?

                Viele Grüße
                Robert

          2. Langsam kloppen sich die Teile um die erste Stelle, denn "session_start()" muss ja auch ganz am Anfang stehen.

            Muss es nicht. Auf jeden Fall - und darauf deutet Deine Beschreibung hin - muss ob_start(); in Deinem Fall vor die erste Ausgabe und session_start(); vor die erste Benutzung von $_SESSION und auch vor der ersten Ausgabe von Payload (z.B. HTML) erfolgen, weil session_start(); ein Cookie erzeugt, welches - „OHA!“ - das Senden einer Headerzeile nach sich zieht. ob_start(); kann also vor session_start(); stehen. Man muss wissen, was was, dieses sodann „wie, wann und warum“ macht.

            1. Ich hab mal den entscheidenden Code hier dargestellt:

              $a_auth = file("tn-auth", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
              if (trim($a_auth[0]) == "tn-auth")# Dateiprüfung
              {
              	foreach ($a_auth as $s_auth)
              	{
              		# $x[0] = Anmeldename, $x[1] = Passwort
              		$x = explode("↔", $s_auth, 2);
              		if (count($x) < 2) continue;
              					
              		$b_name = trim($aa_post["bname"]) == trim($x[0]);
              		$b_pass = trim($aa_post["pass"]) == trim($x[1]);
              					
              		# Nutzername/Passwort prüfen, Nutzer/Zeit speichern
              		if ($b_name && $b_pass)
              		{
              			foreach ($a_user as $s_user)
              			{
              				$patt = '/name_nutzer_b64↔(.+)(?:\s.+)*\szugang_b64↔'.$x[0].'/m';
              				preg_match($patt, $s_user, $treffer);
              				if ($treffer)
              				{
              					ob_start();
              					$sid = trim(substr($s_user, 0, 4), ' {}');
              					session_start();
              					$_SESSION['user_id'] = $sid;
              					$_SESSION['user_nm'] = trim($treffer[1]);
              					$insert = "gnDas Einloggen war erfolgreich!";
              					header('Location: pinwand_html.php');
              					ob_end_flush();
              					#include "pinwand_anm_html.php";
              					exit;
              				}
              			}
              		}
              	}
              	$insert = "Benutzername oder Passwort sind falsch<br>"
              		."oder das Konto existiert nicht.<br>"
              		."Neu versuchen oder ein neues Profil anlegen?";
              	include "pinwand_anm_html.php";
              }
              

              "$insert" ist die Variable, in der ich den anzuzeigenden Text übergebe (nur bei include...) und auch nur dort wird exit; benötigt - klar.
              Vor diesem Text gibt es keine Ausgabe,
              session_start(); bleibt ganz am Anfang der PHP-Datei,
              ob_start(); habe ich also vor den session_start(); gesetzt.
              Ändert aber nix an der fehlfunktion.

              An die error.log auf dem Server komme ich vermutlich nicht 'ran, ich frage aber mal nach. Die error.log im XAMPP habe ich gefunden - Grundgütiger! Der schreibt ja wirklich jede Meldung 'rein. Das sind über 287tsd Zeilen. Aber wenn ich alle Zeilen lösche, die nichts meinem Problem zu tun habe (nicht definierte Variablern, Ein-/Ausschalten des Servers usw.) bleiben immernoch 250 Zeilen übrig.
              Nur werde ich dort keine bezügliche Fehlermeldung finden, auf XAMPP funktioniert's ja.
              Ich bin schlicht ratlos...

              1. Hallo MoaByter,

                na, da tun sich eine Menge Fragen auf...

                $a_auth = file("tn-auth", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
                if (trim($a_auth[0]) == "tn-auth")# Dateiprüfung
                {
                   ...
                }
                else 
                {
                   ??? Was tust Du hier? Also:
                   - wenn $a_auth FALSE enthält
                   - wenn die erste Zeile nicht tn_auth enthält?
                }
                

                Erstens: Wie ist das Errorhandling für die tn_auth Datei? Würdest du es mitbekommen, wenn er in den oben skizzierten else-Pfad gelangte und deine große Prüfschleife ganz überspringt? Eine weiße Seite spricht dafür, dass das passiert sein könnte.

                Zweitens: Die tn_auth Datei enthält User und Passworte. Ich nehme an, base64-codiert, andernfalls würde die Suche nach dem Zugang nicht funktionieren. Und ich nehme an, diese Datei wird von der Benutzerregistrierung gepflegt. Wie ist diese Datei initial auf den Server gekommen? Per FTP Upload? Wird das Trennzeichen ↔ (\u2194) korrekt übersetzt? Kommst Du überhaupt jemals über die if (count($x) < 2) continue; Abfrage hinweg? Du solltest diesen Code Schleife ggf. mal mit Echos spicken, um zu prüfen, was da genau verarbeitet wird. Ja, klar, dann klappt kein Redirect mehr. Aber um erstmal zu schauen, ob er korrekt durchs Programm läuft, ist das hilfreich.

                Drittens: Aus welchem Himmel fällt $a_user? Das scheint ein Array zu sein, wo Benutzernamen und Zugangscodes drin stehen, und du suchst den Benutzernamen zu einem Zugangscode in $x[0].

                • Wo kommt a_user her? Steht da auf dem Server drin, was Du erwartest?
                • Warum baust Du x[0] in die Regexp ein? Ist das ein base64-codierter String? Warum trimmst Du ihn nicht? Oben, beim Vergleich mit aa_post["bname"], tust Du es.
                • Was ist, wenn ein Zugangscode ein Präfix eines anderen Zugangscodes ist (z.B. Zugangscode Wmlya3Vz (Zirkus) vs Wmlya3VzcGZlcmQ= (Zirkuspferd))? Da gehört ein $ ans Ende des Patterns (Anker für Ende des Strings)!
                • Warum das m-Flag? Das bezieht sich auf das Verhalten von ^ und $ in Strings, die Zeilenumbrüche enthalten - ist das hier relevant?

                Viertens: Dein ob_start scheint mir unnötig. Zwischen ob_start und ob_end_flush steht nichts, was rechtmäßig Content-Ausgaben erzeugt - es sei denn, da erscheint eine PHP Fehlermeldung. Aber dann sollte diese Fehlermeldung vermieden werden.

                Fünftens: Warum verwendest Du keine Datenbank für die Benutzerverwaltung? Das dürfte weniger schmerzhaft sein als das Gefummel mit Dateien. Alternativ könntest Du deine Daten auch als ordentliches assoziatives Array aufbauen, dieses Array mit serialize plattklopfen und in eine Datei dumpen. Zur Wiederverwendung liest Du einfach das Serialisat ein und machst mit unserialize() wieder ein Array draus. Die Alternative wäre json_encode und json_decode, um das Array als JSON-String zu speichern. Beide Varianten kümmern sich selbstständig um Zeilenumbrüche, Anführungszeichen und andere Merkwürdigkeiten.

                Rolf

                --
                sumpsi - posui - obstruxi
              2. Moin,

                Ich hab mal den entscheidenden Code hier dargestellt:

                … der nicht nur mit ~~~, sondern ~~~php sogar farbig wird …

                $a_auth = file("tn-auth", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
                
                • Was passiert, wenn die Datei aus irgendeinem Grund nicht gelesen werden kann?
                • Liegt die Datei im gleichen Verzeichnis wie dein Skript und ist beides von außen zugreifbar? Kann ich also in der Adresszeile des Browsers einfach den Namen deines Skripts durch tn-auth ersetzen und bekomme dann die Datei angezeigt?
                if (trim($a_auth[0]) == "tn-auth")# Dateiprüfung
                

                Was soll das genau prüfen?

                {
                	foreach ($a_auth as $s_auth)
                	{
                		# $x[0] = Anmeldename, $x[1] = Passwort
                		$x = explode("↔", $s_auth, 2);
                		if (count($x) < 2) continue;
                					
                		$b_name = trim($aa_post["bname"]) == trim($x[0]);
                		$b_pass = trim($aa_post["pass"]) == trim($x[1]);
                					
                		# Nutzername/Passwort prüfen, Nutzer/Zeit speichern
                		if ($b_name && $b_pass)
                

                An dieser Stelle stimmen also „bname“ und „pass“ mit den Werten aus der Datei überein. Warum musst du jetzt noch einmal durch alle Nutzer iterieren?

                		{
                			foreach ($a_user as $s_user)
                			{
                				$patt = '/name_nutzer_b64↔(.+)(?:\s.+)*\szugang_b64↔'.$x[0].'/m';
                

                Ist sichergestellt, dass in $x[0] nichts steht, dass hier deinen regulären Ausdruck stören kann?

                An die error.log auf dem Server komme ich vermutlich nicht 'ran, ich frage aber mal nach.

                Aber das error_reporting und Prüfen von Rückgabewerten kannst du in deinem Script tun.

                Viele Grüße
                Robert

          3. Lieber MoaByter,

            1. ohne POST-Übergabe wird das Anmelde-HTML eingelesen (mit exit),
            2. Dateiupload für Profil-Erstellen mit Avatar (kein exit, die Datenverabeitung folgt),
            3. Das Einlesen der übergebenen Profil-Daten der Nutzer (mit exit),
            4. Das Speichern der übergeben Daten der Beiträge der Nutzer, mit exit, denn dann ist Schluss.

            wenn Du exit verwenden musst, dann hat Dein Programm ein Problem mit unpassenden Strukturen. Nach speziellen header()-Aufrufen lasse ich mir das noch gefallen, aber ansonsten sollte Dein Programm immer regulär zuende laufen dürfen.

            $buffer = '<!doctype html><html>...</html>';
            $redirect = '';
            $send_data = true;
            
            ...
            
            if (!empty($redirect)) {
              header('Location: '.$redirect);
              $send_data = false;
            }
            
            if (array_key_exists('js', $_GET)) {
              $buffer = 'window.print();';
              header('Content-Type: text/javascript; charset=utf-8');
            }
            
            if ($send_data) {
              echo $buffer;
            }
            
            // end of program
            

            Dann können nur noch PHP-Fehlermeldungen dafür sorgen, dass die Header schon gesendet wurden und die Weiterleitung nicht mehr klappt. Aber dann hast Du eben PHP-Fehler, die behoben werden müssen, was an sich bereits ein anderes Problem ist.

            Liebe Grüße

            Felix Riesterer

            1. Danke für die schnelle Antwort.
              Das sieht ja nun ganz anders aus, die Beschreibungen im Netzt hatten das nicht drin.

              Das exit; dient nur dem include ..., damit er danach nicht den folgenden Code durchläuft. Ein schnelles Ende sozusagen.

              In den $bufferkommt also die gesamte Seite 'rein? Zum Beispiel mit

              file_get_contents(xyz.php);
              

              Im $buffer müsste ich also die php-Teile bearbeiten? Diese dienen dazu, bestimmte Indices einzufügen.

              Und dem $redirectwird die Seitenadresse zugewiesen? Denn leer darf's ja nicht bleiben, da sonst nix gesendet wird.

              Bis zum header(...)-Befehl wird bei mir ja nix gesendet, also gibt's auch keine Fehlermeldung über gesendetet Daten. Das mus ich mal ausprobieren.

              1. Lieber MoaByter,

                Das exit; dient nur dem include ..., damit er danach nicht den folgenden Code durchläuft. Ein schnelles Ende sozusagen.

                das verursacht vor allem später neue Probleme. Angenommen, Du willst nach dem Abarbeiten eines includierten Scripts eine Testausgabe (Log/Debug etc.) in eine Datei machen. Durch das exitwird das unmöglich.

                In den $bufferkommt also die gesamte Seite 'rein?

                Wenn Du mit „Seite“ das HTML-Dokument (oder was auch immer PHP ausgeben soll) meinst, ja.

                file_get_contents(xyz.php);
                

                Das ist grober Unfug. PHP-Scripte sollen ja geparst und ausgeführt werden. Was soll ich im Script mit seinem Quelltext?

                Und dem $redirectwird die Seitenadresse zugewiesen? Denn leer darf's ja nicht bleiben, da sonst nix gesendet wird.

                Das ist ein Vorschlag von mir, um Dir zu zeigen, wie man trotz eines Redirects ein PHP-Script nicht mit exit abbrechen muss. Wie Du das tatsächlich handhabst, überlasse ich komplett Dir. Im Beispiel sollte nur klar werden, dass man das Ausgeben von Daten mittels eines geeigneten Programmverlaufs vermeiden kann, wenn im Script per HTTP-Header eine Weiterleitung veranlasst werden soll, denn in diesem Fall hätte ein (vollständiges) HTML-Dokument kaum einen Sinn, weil es der Benutzer ja nicht angezeigt bekommt.

                Bis zum header(...)-Befehl wird bei mir ja nix gesendet, also gibt's auch keine Fehlermeldung über gesendetet Daten.

                Befehle gibt es auf der Kommandozeile. In einem Script spricht man üblicherweise von Anweisungen. Der Aufruf einer Funktion wie header() ist eine solche Anweisung.

                Wenn PHP einen Fehler an den Browser ausgibt, dann kommt es unerwarteter Weise eben doch zu Ausgaben vor Deinem header-Aufruf. Aber wenn Du das sicher ausschließen kannst, dann ist eine Ursache ja schon sicher ausgeschlossen.

                Das mus ich mal ausprobieren.

                Unbedingt!

                Liebe Grüße

                Felix Riesterer

      3. Moin,

        Wenn ich das Ding ganz an den Anfang setze, geht's:

        Dann wird wohl irgendwo schon was ausgegeben – du solltest dich mit dem EVA-Prinzip beschäftigen: jegliche Ausgaben dürfen erst stattfinden wenn die Verarbeitung der Daten fertig ist und damit z.B. feststeht ob zu einer anderen Seite weitergeleitet werden muss oder nicht.

        Passwörter: Interssiert keine, das ist alles noch Testphase, die PWs sind "raupe", "anne" und so schwierige Wörter. alles nur Test.

        Trotzdem solltest du es gleich richtig machen und password_hash() bzw. password_verify() verwenden.

        Mit "Sicherheit" habe ich mich vermutlich noch nicht ausreichend befasst, schütze das vor Hackereingaben (diese Seite ist tief vergraben auf dem Server) lediglich mit

        htmlspecialchars($_POST);
        

        Das reicht nicht. Beschäftige dich mit dem Thema Kontextwechsel und wie sie korrekt behandelt werden.

        Gruß
        Tobias

        1. Das EVA-Prinzip? Grundgütiger, hat die mit dem Apfel nicht schon genug... egal.

          Bei PHP bleibt mir nix anderes übrig, als nach dem EVA-Prinzip zu verfahren, oder? Der Nutzer sendet seine Eingaben per "submit" an den Server, der schaut sich alles an und wenn die Eingaben sinnvoll und schlüssig sind, sendet er (hoffentlich) die richtige Webseite an den Nutzer zurück. Wie soll das anders gehen? Siehe auch meine Antwort auf Raketenwillis Beitrag.

          Die Anweisung "header(...)" hat keine Rückgabe - wo darf ich denn was erwarten?

          Im Moment bin ich bei den Sessions und Session-Cookies. Noch funktioniert's nicht richtig: Wenn der Nutzer den Browser schließt, sollte das session-Cookie gelöscht sein. Wenn er - oder sie - den Browser wieder öffnet (bei mir werden die Tabs Weieder hergestellt), sollte die Webseite prüfen, ob der Nutzer eingeloggt ist. Ist er nicht! Also sollte auf die Anmeldeseite verzweigt werden, das aber funktioniert tatsächlich nur mit 'nem meta-Eintrag "redirect usw.". Javascript tut's einfach nicht: "location-href macht's nicht, location.replace() auch nicht. Naja, kleines Problem.

          Beim Prüfen der Session muss ich die PHPSESSIONID mit einbeziehen, oder? Also die Zuordnung Session-Id zu Nutzername/-Passwort.

          Wenn das funktioniert, kommt das Sichern der Passwörter dran. Da muss ich nch einiges lesen. Ich habe zwar ein ziemlich dickes Buch hier liegen - PHP 7 und MySQL, bin da aber erst auf Seite 300 oder so von etwa Tausend. Aber es gibt ja auch php.net und andere, die teils wirklich gutes Zeug verfassen. php.net ist für mich oft schwierig zu verstehen, da sie 100tsd andere Sachen mit einbeziehen, die die Hinweise zwar vervollständigen, aber den Anfänger ziemlich verwirren. Und vor den "Kontext"-Geschichten graut mir noch etwas, ich muss erstmal verstehen, was damit eigentlich gemeint ist.

          Selbstverständlich Danke für deinen Beitrag, wie du siehst, bleibe ich dran, wird nur etwas dauern.

      4. Moin,

        Mit "Sicherheit" habe ich mich vermutlich noch nicht ausreichend befasst, schütze das vor Hackereingaben (diese Seite ist tief vergraben auf dem Server) lediglich mit

        htmlspecialchars($_POST);
        

        Das schützt eigentlich nur vor funktionierendem Code, denn htmlspecialchars akzeptiert kein Array, sondern nur einen String als ersten Parameter. Und es maskiert lediglich einige Zeichen, die im HTML-Kontext eine besondere Bedeutung haben. Wenn deine Daten in einem anderen Kontext landen, brauchst du den passenden Kontextwechsel.

        Viele Grüße
        Robert

        1. Hallo,

          htmlspecialchars($_POST);
          

          Das schützt eigentlich nur vor funktionierendem Code, denn htmlspecialchars akzeptiert kein Array, sondern nur einen String als ersten Parameter.

          Dann konvertiert PHP automatisch und erhält in diesem Fall vermutlich den String "Array". Aber solange man das Ergebnis von htmlspecialchars() sowieso nicht verwendet, sondern einfach fallen lässt, ist es komplett wirkungslos.

          Einen schönen Tag noch
           Martin

          --
          Kaffee ist nur schädlich, wenn Ihnen ein ganzer Sack aus dem 5. Stock auf den Kopf fällt.
  3. Lieber MoaByter,

    if (pw_ein == pw_speicher)
    

    Du hast tatsächlich zwei Konstanten definiert, die Du hier vergleichst? Die Namen pw_ein und pw_speicher haben kein Dollar-Zeichen, sind in PHP also Konstanten. Oder ist das bereits eine Quelle von Fehlermeldungen und unerwartetem Programmverhalten?

    Um ein eingegebenes Passwort mit einem hinterlegten Hash zu vergleichen, verwendet man in PHP die Funktion password_verify(). Um anstelle der Passwörter deren Hash zu speichern, verwendet man password_hash().

    Liebe Grüße

    Felix Riesterer

    1. Nein, keine Konstanten, sondern vergessene $-Zeichen. Ich wollt's hier nur symbolisch darstellen. Bitte - das Ganze ist in der Entwicklungs- und Testphase, ich bin neu mit php, da dauert's etwas länger. Und wie gesagt, auch die eingebenen Passwörter sind symbolisch, nix wird real verwendet. Den Link dazu kennt auch niemand außer mir und 'nem Freund, der hilfreich als Tester zur Seite steht. All die Feinheiten wie Passwortschutz usw. kommen noch.