claus ginsel: PHP: default Zweig in switch will nicht anspringen

Guten Morgen

ich stöbere gerade in der access.log meines Hosters. Und da fielen mir Zugriffe wie folgt auf:

*.*.*.* [24/Mar/2022:00:12:27 +0100] "GET /index.php?kap=/etc/passwd HTTP/1.1" 403 7203 " " "Mozilla/5.0 (Windows NT 10.0; Win64; X64; Rv:85.0) Gecko/20100101 Firefox/85.0"

oder

*.*.*.* [24/Mar/2022:00:12:06 +0100] "GET /index.php?kap=sub HTTP/1.1" 200 7218 " " "Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)"

Im 1. Beispiel hat der Webserver ja richtig reagiert. Aber im 2. Fall sollte eigentlich auch 403 gesetzt werden.

Es ist nur ein GET-Parameter vorgesehen, der ist immer 3 Zeichen lang. Hier das Script dazu:

if(isset($_GET['kap'])) {

	if(strlen($_GET['kap']) > 3 ) exit(header('Status: 403', TRUE, 403));

	// homjhjhj erfüllt sonst case hom

	switch ($_GET['kap']) {
		case "hom": include('kapitel/kap_home.php'); break;

		case "ba1": include('kapitel/kap_basics1.php'); break;

		case "ba2": include('kapitel/kap_basics2.php'); break;

		default: exit(header('Status: 403', TRUE, 403));
	}
}

habe dann probiert mit

if(preg_match('/^[a-z1-6]{3}$/', $_GET['kap'])==false)
exit(header('Status: 403', TRUE, 403));

wurde 403 auch nicht ausgelöst.

Das Problem ist das Leerzeichen im URI, sehe ich das richtig? Wie würdet Ihr die Prüfung durchführen?

Gruß Claus

Edit Rolf B: Code in ~~~php / ~~~ eingeschlossen
Edit Rolf B: IP ist personenbezogen - gelöscht, muss anonymisiert geloggt werden!

akzeptierte Antworten

  1. <?php
    $_GET['kap'] =  "hom324234";
    if( isset( $_GET['kap'] ) ) {
    
    	switch ( $_GET['kap'] ) { 
    
    		case "hom": echo( 'kapitel/kap_home.php' ); break;
    
    		case "ba1": echo( 'kapitel/kap_basics1.php' ); break;
    
    		case "ba2": echo( 'kapitel/kap_basics2.php' ); break;
    		
    		default: echo ('Status: 403');
    	}
    }
    

    In meinen Tests funktioniert das Du es wohl erwartest. Weder ist der zusätzliche exit bei einer Länge von als 3 Zeichen nötig noch wird der default-Zweig ignoriert.

    Offenbar liegt die Chose an anderer Stelle. Auch ein Wert wie '/etc/passwd HTTP/1.1' ändert nichts daran, dass der Code funktioniert.

    Aber

    exit(header('Status: 403', TRUE, 403));
    

    ist Unsinn.

    Du willst:

    http_response_code( 403 );
    echo( 'Status: 403' );
    exit;
    
    1. Hallo raketenwilli

      probier doch mal mit

      sub HTTP/1.1

      Hierbei kommt der default Zweig nicht.

      Was macht denn exit(header('Status: 403', TRUE, 403)); falsch?

      1. Hallo raketenwilli

        probier doch mal mit

        sub HTTP/1.1

        funktioniert.

        Hierbei kommt der default Zweig nicht.

        Nein. Er wird gewählt. Frage an Dich: Spielt Dir ein Cache einen Streich?

        Was macht denn exit(header('Status: 403', TRUE, 403)); falsch?

        exit

        gibt laut Handbuch einen Status zurück, nicht aber den HTTP-Statuscode.

        Das wird benutzt, um beim Ausführen von PHP als cli-Skript in einer Shell oder als Subroutine signalisieren, ob das Skript fehlerfrei durchlief.

        Wann immer der Wert auf eine Zahl verschieden von 0 gesetzt wird, wird also in einer Shell, Pipe, if- oder where-Konstrukt oder sonstwas dem Elternprozess ein fehlerhafter Programmablauf signalisiert:

        #!/bin/bash
        if [ php test.php ]; then
            echo "Hurra";
        else
            echo "Fehler";
        fi
        

        gibt also „Fehler“ aus, wenn die test.php mit exit(1) beendet wird. In einer Shell kann man den Status übrigens mit etwas wie echo $? abfragen.

        Also notierst Du sowas:

        http_response_code( 403 );
        echo( 'Status: 403' ); # Ausgabe auf der Webseite
        exit;
        

        oder sowas:

        header( 'HTTP/1.1 403 Forbidden', TRUE, 403 );
        echo( 'Status: 403' ); # Ausgabe auf der Webseite
        exit;
        
        1. Hallo Raketenwilli,

          http_response_code( 403 );

          Das ist nun interessant.

          header("Status:403", true, 403);

          sollte ja nur wegen seines 3. Parameters wirksam sein, Einen "Status" Header gibt's nicht. Und im PHP Handbuch steht auch nichts von einem Status-Pseudoheader, den PHP da verarbeitet. Stand das mal drin? Denn unter IIS mit FastCGI ist es so:

          http_response_code(404) erzeugt HTTP/1.1 404 Not Found

          header("Status: 404") erzeugt HTTP/1.1 404

          header("Status: 404", true, 403) erzeugt HTTP/1.1 404
          header("Stuss: 404", true, 403) erzeugt HTTP/1.1 403

          Whoa - der 3. Parameter ist nachrangig! Ist das etwas, was der IIS zaubert?

          und

          header("HTTP/1.1 404 Hau doch ab!") erzeugt
          HTTP/1.1 404 HTTP/1.1 404 Hau doch ab!

          Einen Header "Status" zeigt mir Chrome in keinem Fall an, ich bin jetzt aber auch nicht mit dem Drahthai auf die Suche gegangen.

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Tjs.

            http_status_code(403);
            

            oder

            header('HTTP/1.1 403');
            

            oder

            header('HTTP/1.1 403 Forbidden');
            

            führt zu:

            HTTP/1.1 403 Forbidden

            im Response-Header.

            (mit Einschränkungen klappt auch:)

            header('HTTP/1.1 403 Irgendwelcher Mist');
            

            Zumindest mit wget kümmert sich nicht um den Text. Chrome auch nicht. Der Status wird auf 403 gesetzt, der Fehler erkannt.

            Der meint, dass man die Variante von Claus nutzen solle, wenn PHP als FastCGI und nicht als Apache Modul ausgeführt würde.

            header("Status:403")
            

            wäre Müll, denn das führt bei PHP als Modul zu einem Statuscode 200 (OK).

            Der meint, dass man die Variante von Claus nutzen solle, wenn PHP als FastCGI und nicht als Apache Modul ausgeführt würde.

            Ok. FastCGI. Bisher war alles mit PHP als Modul. Ich teste das auf einem Server mit FastCGI …

            <?php
            header("Status: 403  irgendwelcher Unsinn");
            ?>
            Nicht vorhanden.
            

            Hm. Das geht tatsächlich auf dem anderen Server.

            Das funktioniert also nur mit FastCGI . Warum zum Teufel sollte ich etwas tun, was ein erheblicher Mehraufwand ist und nur unter bestimmten Bedingungen zum erwarteten Ergebnis führt?

            Das gezeigte:

            header('HTTP/1.1 403');
            

            erschlägt, wie Du mit dem „patsch!“ wohl gemeint hast, das Problem auf allen Servern und ich sehe außer, um Spaß (oder bei Irrtümern oder einem Seitenumzug eben Ärger) zu haben, keinen Grund was anderes zu fummeln.

            Mit seiner Empfehlung ist der andere Autor eben anderer Meinung...

            1. Hallo Raketenwilli,

              danke für die Rückmeldung.

              Falls sich jemand wundert: Ich habe den Link auf Sistrix wieder rausgenommen (weil es eben tatsächlich funktioniert hat) und du hast auf die Vorversion geantwortet. Die steht noch in der Versionshistorie meines Postings.

              Rolf

              --
              sumpsi - posui - obstruxi
            2. Das gezeigte:

              header('HTTP/1.1 403');
              

              erschlägt, wie Du mit dem „patsch!“ wohl gemeint hast, das Problem auf allen Servern

              Hm. Copy+Paste-Error auf Level 8:

              http_response_code( 403);
              

              war gemeint.

              Und das von mir ursprünglich gebrachte

              http_status_code( 403);
              

              funktioniert auch nur dann, wenn es eine PHP-Version meiner Phantasie mit einem solchen Alias gibt.

            3. So. Ich habs:

              File: fake404.cgi

              #!/usr/bin/php
              Status: 404
              Content-Type:text/html; charset=utf-8
              
              <h1>Hier wird ein 404er gefälscht.</h1>
              <hr>
              <?php echo date('Y-m-d H:i:s');?>
              

              (Das ist nicht im eigentlichen Sinne falsch, der Code funktioniert - es soll nur keiner nachmachen, der nicht meinen Test wiederholen will.)

              Die Programmiersprache ist egal, das würde mit Perl, Bash, Javascript (node.js), Python ... auch so klappen (ich hätte dann aber mit print bzw. echo einige Mühe...). PHP gibt die Zeilen außerhalb <?php ... ?> 1:1 zurück. Das mache ich mir oben zu nutze.

              Ausgaben im Terminal nach chmod 755 fake404.cgi; ./fake404.cgi

              Status: 404
              Content-Type:text/html; charset=utf-8
              
              <h1>Hier wird ein 404er gefälscht.</h1>
              <hr>
              2022-03-24 13:40:08
              

              ... abholen via Webserver:

              < HTTP/1.1 404 Not Found
              < Date: Thu, 24 Mar 2022 12:45:52 GMT
              < Server: Apache/2.4.52
              < Permissions-Policy: interest-cohort=()
              < Transfer-Encoding: chunked
              < Content-Type: text/html; charset=utf-8
              < 
              <h1>Hier wird ein 404er gefälscht.</h1>
              <hr>
              2022-03-24 13:42:33
              

              ( curl -v markiert die Response-Headerzeilen mit vorangestelltem '< ').

              Das entspricht dem, was ich einst mit Perl lernte. Die erste leere Zeile im Ausgabestrom eines CGI-Skriptes trennt den HTTP-Header vom Payload (oft aber nicht immer: der Webseite).

              Es ist hier das CGI-Modul des Webservers, welches den Datenstrom entgegen nimmt und die HTTP-Header ersetzt bzw. ergänzt. Also nicht PHP. Übrigens ist eine Zeile mit dem Content-Type verpflichtend, sonst: „Error 500“. Dieses Problem wurde in Zeiten, als Perl/Cgi noch in Mode war, in diesem Forum ganz oft auf den Tisch gebracht...

              (Und schaut mal hier, in die alte Doku, die Google sehr gerne findet: http://www-hera-b.desy.de/subgroup/computing/IT/www/selfhtml/cgiperl/sprache/cginotwendig.htm)

              Insofern braucht sich niemand wundern, warum etwas wie "Status: 404" im PHP-Handbuch nicht vorkommt: PHP hat damit nichts zu tun. Im Apache Handbuch, den Seiten zu *-cgi, sollte aber dazu was stehen.

              1. Im Apache-Handuch steht fast nichts. Aber in der RFC3875 für CGI, dort bei Punkt 3.6.6.

                Wenn man allen Ernstes PHP als CGI mit dem Apache und mod_cgi benutzen sollte, dann hat man ein Problem:

                • http_response_code(404) bewirkt nichts.
                • Die komplette Funktion header() ist völlig nutzlos.
                • HTTP/1.1 404 Not Found in den Header zu schreiben führt zu einem Error 500. (Serverfehler)
                • Aber status: 404 statisch in den Header zu schreiben (oder dort per echo auszugeben) funktioniert…

                Alles mit und ohne ob_start() getestet.

                Mit „PHP 8.1 als cgi-fcgi“ hingegen funktionieren

                • http_response_code(404)
                • header(HTTP/1.1 404 Not Found)
                • header('status: 404')

                mit jeweils dem selben positiven Ergebnis.

                Mit „PHP 8.1 als Modul“ funktionieren:

                • http_response_code(404)
                • header(HTTP/1.1 404 Not Found)

                Da jetzt das gezeigte Ausführen von PHP innerhalb des oldscool-Modules mod_cgi allenfalls experimentellen Charakter hat (und ergo wirklich alles andere als die Regel ist) würde ich bei den beiden zuletzt genannten Möglichkeiten bleiben, die mit PHP als Modul aber auch mit cgi_fcgi tun, was sie sollen.

          2. ich bin jetzt aber auch nicht mit dem Drahthai auf die Suche gegangen.

            wget -d oder besser noch curl -v (das kann sogar HTTP2! -> Ausgabe HTTP/2 403) reichen völlig aus. Da musst Du kein Monster wie tcpdump mit grafischer Oberfläche anwerfen…

  2. (Gepostet, als von Raketenwillis Posting erst der Stand von 9:09 sichtbar war, daher einige Redundanzen damit)

    Hallo claus,

    if(strlen($_GET['kap']) > 3 )
    exit(header('Status: 403', TRUE, 403));
    // homjhjhj erfüllt sonst case hom

    Sagt wer?! PHP von v4.4.9 bis 8.1.3 machen keinen Prefixvergleich, sondern einen vollständigen Vergleich. Diese Abfrage ist unnötig.

    if(isset($_GET['kap'])) { ... }

    Was tust Du, wenn der Parameter nicht gesetzt ist? Wird dieser Fall außerhalb dessen, was Du vorgezeigt hast, behandelt? Dies ist einer von zwei Fällen, für die ich mir vorstellen kann, dass der Header nicht gesetzt wird (der andere kommt weiter unten). Aber in deiner gezeigten Situation ist der Parameter da, das ist also aktuell nicht das Problem.

    exit(header(...));

    Wrong. Die Funktion header liefert void - also gar nüscht - zurück. Ihr Ergebnis kann daher nicht als Argument für eine andere Funktion verwendet werden. Mach es so, auch wenn das zwei Zeilen sind:

    header(...);
    exit();
    

    Ansonsten bin ich der Raketenmeinung: irgendwas anderes ist das Problem. Der gezeigte Code sollte bei index.php?kap=sub definitiv einen 403 Status erzeugen. Das könnte höchstens dann schiefgehen, wenn vor dem header-Aufruf eine andere Ausgabe gemacht wurde, die dazu führte, dass die Header bereits gesendet wurden. In dem Fall ist ein header-Aufruf wirkungslos.

    Das Problem ist das Leerzeichen im URI, sehe ich das richtig?

    Ich sehe kein Leerzeichen in der URI. Wo ist eins?

    Kann es sein, dass Du vor dem gezeigten Code noch etwas machst, das Ausgaben produziert, wenn der kap-Parameter vorhanden und nicht länger als 3 Stellen ist?

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Das könnte höchstens dann schiefgehen, wenn vor dem header-Aufruf eine andere Ausgabe gemacht wurde, die dazu führte, dass die Header bereits gesendet wurden. In dem Fall ist ein header-Aufruf wirkungslos.

      Jein. Genauer: Regelmäßig schon seit einigen PHP-Versionen nicht mehr unbedingt. Schon eine ganz Weile ist das „output-buffering“ (nur nicht in der Shell-Umgebung also „CLI-SAPI“, da gibt es keine Header) standardmäßig aktiviert - die Ausgaben gehen erst nach Ende des Skriptes (oder bei Erreichen der in der php.ini gesetzten Grenze) „raus“ und die Headerzeilen können von PHP also (womöglich!) noch ersetzt werden.

      ; Output buffering is a mechanism for controlling how much output data
      ; (excluding headers and cookies) PHP should keep internally before pushing that
      ; data to the client. If your application's output exceeds this setting, PHP
      ; will send that data in chunks of roughly the size you specify.
      ; Turning on this setting and managing its maximum buffer size can yield some
      ; interesting side-effects depending on your application and web server.
      ; You may be able to send headers and cookies after you've already sent output
      ; through print or echo. You also may see performance benefits if your server is
      ; emitting less packets due to buffered output versus PHP streaming the output
      ; as it gets it. On production servers, 4096 bytes is a good setting for performance
      ; reasons.
      ; Note: Output buffering can also be controlled via Output Buffering Control
      ;   functions.
      ; Possible Values:
      ;   On = Enabled and buffer is unlimited. (Use with caution)
      ;   Off = Disabled
      ;   Integer = Enables the buffer and sets its maximum size in bytes.
      ; Note: This directive is hardcoded to Off for the CLI SAPI
      ; Default Value: Off
      ; Development Value: 4096
      ; Production Value: 4096
      ; https://php.net/output-buffering
      output_buffering = 4096
      

      (Auszug aus meiner originalen PHP.ini)

  3. Hallo claus,

    eine von der Frage unabhängige Info: Ich habe die von dir geposteten IPs gelöscht. Eine IP zusammen mit einem Timestamp ist ein personenbezogener Wert und darf nur mit Einwilligung gespeichert werden. Die musst Du anonymisieren.

    Ich weiß nicht, wie die Hoster das machen - rein formal darf die IP überhaupt nicht unanonymisiert auf die Festplatte.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo

      Ich weiß nicht, wie die Hoster das machen - rein formal darf die IP überhaupt nicht unanonymisiert auf die Festplatte.

      Wie kommst du darauf? Es gibt schließlich berechtigte Interessen. Die müssen offengelegt werden (Datenschutzerklärung) und ermöglichen die Speicherung der IP (und anderer Daten).

      Tschö, Auge

      --
      200 ist das neue 35.
      • Es war richtig, die fremde IP zu löschen.

      Ich weiß nicht, wie die Hoster das machen - rein formal darf die IP überhaupt nicht unanonymisiert auf die Festplatte.

      Bis zu zwei Wochen gilt das „berechtigte Interesse“ an der Nachverfolgung von Angriffen. Danach hilft sed -i … um die IPs in den Logs zu anonymisieren (ist ja alles nur Text und logrotate kann sowas als Skript.

      Bei mir zu Hause „leben“ die Logfiles des Apache nur zwei Tage.

      1. Hallo Raketenwilli,

        ah. Danke 😀

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Rolf, ich versuche daran zu denken, beim nächsten Mal die IP rauszunehmen.

          Mein "Leerzeichen" von der Eingangsfrage war eine Falschinterpretation, ich hatte "HTTP/1.1" als Teil des URI angesehen. Und "sub" ist gültig. Damit reagiert switch ja doch, wie es soll.

          Eure Ausführungen bezüglich exit und dem Setzen des Statuscodes schau ich mir in Ruhe an. Ich habe kürzlich diese Form eingeführt, weil ich weg wollte von selbst gebauten Fehlerseiten und stattdessen die Standardseiten des Webservers angezeigt haben will, mit dem Nebeneffekt, dass das aktive Setzen von 403 auch durch mod_qos erfasst wird.

          Gruß Claus

          1. Hallo claus,

            danke für die Rückmeldung. Dein geposteter Sourcecode war also eine "Zusammenfassung" des realen Codes?

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo Rolf

              ja, viel php ist aber nicht.

              Warum fragst Du?

              Claus

          2. Hallo Claus,

            Eure Ausführungen bezüglich exit und dem Setzen des Statuscodes schau ich mir in Ruhe an.

            exit(header('Status: 403', TRUE, 403));
            

            Ich habe kürzlich diese Form eingeführt, weil ich weg wollte von selbst gebauten Fehlerseiten und stattdessen die Standardseiten des Webservers angezeigt haben will, mit dem Nebeneffekt, dass das aktive Setzen von 403 auch durch mod_qos erfasst wird.

            Den HTTP-Statuscode musst du in beiden Fällen setzen, egal ob du eigene Fehlerseiten haben willst oder nicht. Aber die Kombination aus header() und exit() ist, so wie du sie notierst, syntaktisch unsinnig. Sie bedeutet: Rufe erst die Funktion header() auf, und setze dann ihren Rückgabewert als Exit-Code des Scripts. Nur ist der Exit-Code des Scripts im Web-Umfeld bedeutungslos, und außerdem liefert header() überhaupt kein Ergebnis.

            Einen schönen Tag noch
             Martin

            --
            Мир для України.
            1. Hallo Martin

              ich schreib das um.

              Es scheint aber eine durchaus häufige Praxis zu sein: stackoverflow

              Gruß Claus

            2. Hallo Nochmal

              eine Nachfrage zum Setzen Statuscode, wann nehm ich welche Variante:

              header("HTTP/1.1 404 Not Found");

              header('Status: 403', TRUE, 403);

              http_response_code( 403 );

              Header soll man ja vor jeglicher Ausgabe setzen, wie ist das bei diesem speziellen (bei mir ist Ausgabe davor, trotzdem war der Statuscode gesetzt worden)?

              kann http_response_code auch nach Ausgabe gesetzt werden (klappt bei mir trotz Ausgabe davor)?

              Claus

              1. Hallo,

                eine Nachfrage zum Setzen Statuscode, wann nehm ich welche Variante:

                header("HTTP/1.1 404 Not Found");

                hier meinst du vermutlich "403 Forbidden". 404 ist etwas anderes.

                header('Status: 403', TRUE, 403);

                Das scheint ja nur in bestimmten Konfigurationen zu funktionieren, wenn ich die Tests vom Raketenwilli richtig deute. Also nicht empfehlenswert.

                http_response_code( 403 );

                Das ist wohl die einfachste Variante.

                Header soll man ja vor jeglicher Ausgabe setzen, wie ist das bei diesem speziellen (bei mir ist Ausgabe davor, trotzdem war der Statuscode gesetzt worden)?

                In neueren PHP-Versionen ist Output Buffering anscheinend als Default aktiv, so dass HTTP-Header auch nach der ersten Ausgabe noch gesetzt werden können.

                kann http_response_code auch nach Ausgabe gesetzt werden (klappt bei mir trotz Ausgabe davor)?

                Ja, wenn die Ausgabe kürzer ist als der Output Buffer (Default 4k).

                Einen schönen Tag noch
                 Martin

                --
                Мир для України.
                1. Danke Martin

                  Gruß Claus

                2. header('Status: 403', TRUE, 403); Das scheint ja nur in bestimmten Konfigurationen zu funktionieren,

                  Mit PHP als Modul und FCGI geht es.

                  Aber: Es ist nicht wirklich sinnvoll. Das hat sich mal jemand ausgedacht, der mit Zeilen knausert und die gesendete Headerzeile ('Status: 403') ist zumindest anno 2022 auch in FCGI „hyperliquid“. Was laut curl -v in beiden Fällen ankommt ist nur 'HTTP/2 403'.

                  Warum also so viel schreiben, wenn es http_response_code( 403 ) „tut“?

                  Hinzu kommt: Wenn eines schönen Tages die Macher von PHP auf die Idee kommen, header() statt wie derzeit nichts vielleicht etwas anderes als eine Zahl zurückgeben zu lassen, dann muss man sich den ganzen Mist wegen der notlos-mutigen Klammersetzung in

                  exit( header( 'Status: 403', TRUE, 403 ) );
                  

                  umschreiben oder sich jemanden bestellen, der ein Skript schreibt, welches den Mist sucht und ersetzt...

                  header('Humbug: 403', TRUE, 403);
                  

                  erzeugt wenigstens was:

                  < HTTP/2 403 
                  < humbug: 403
                  

                  ... man soll dann aber

                  header('X-Humbug: Play with me!', TRUE, 403);
                  

                  verwenden. Damit der IE, Bing, Cortana und Netscape 3.0 Gold-Edition wegen des unbekannten Headers nicht abstürzen. 🤣🤣🤣🤣

                  < HTTP/2 403 
                  < x-humbug: play with me!
                  
                  1. Moin Raketenwilli

                    that's it.

                    Gruß Claus

  4. Lieber claus,

    mir wäre das Script zu unübersichtlich. Warum definierst Du kein assoziatives Array, welches die zu inkludierenden Skripte enthält?

    $inc = [
      'hom' => 'home',
      'ba1' => 'basics1',
      'ba2' => 'basics2'
    ];
    
    if (!array_key_exists($_GET['kap'], $inc)) {
      exit(header('Status: 403', TRUE, 403));
    }
    
    include('kapitel/kap_'.$inc[$_GET['kap']].'.php');
    

    Jetzt sieht man sehr schön, dass ein ungültiger Wert in kap zu einem exit mit 403-er führt. Danach wird das garantiert Passende inkludiert. Keine umschweifige case-Notation, kein default-Zweig oder mehrfache exit-Anweisungen mehr...

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix

      ja, das sieht sehr gefällig aus.

      Wie sieht es aus mit copyright? 😉

      Gruß Claus

      1. Hallo Claus,

        ja, das sieht sehr gefällig aus.

        Wie sieht es aus mit copyright? 😉

        das erreicht nicht die nötige Schöpfungshöhe. 😁
        Ernsthaft: Wer hier Lösungs- oder Verbesserungsvorschläge postet, muss auch damit rechnen, dass sie angenommen werden. Das ist ein inhärentes Risiko.

        Einen schönen Tag noch
         Martin

        --
        Мир для України.
        1. Tja Felix, Martin hat's gesagt!

          Schönen Feierabend Gruß Claus

          1. Lieber Claus,

            Tja Felix, Martin hat's gesagt!

            so war es auch gedacht. :-) Und die leichte Erweiterbarkeit ist auch ein beabsichtigter Bonus... ;-)

            Liebe Grüße

            Felix Riesterer