alexx: UTF8 - Sonderzeichenformatierung

Hallo,

typisches Sonderzeichen Problem, ein Ä wird zum ä. Das Ä kommt aus einer Kommentarfunktion, die eigentlich absolut sauber ist, ich kann mir nicht erklären wie das Ä sich verwandeln kann.

Folgender Ablauf ergibt sich:
1 Aufruf des Kommentarformulares
http-Header=Content-Type: text/html; charset=utf-8
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

2 Senden des Kommentarformulares
http-Header=Content-Type: text/html; charset=utf-8
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

an Mysql auch hier ist alles utf8_gerenal_ci

4 Auslesen bzw. Anzeigen des Kommentares
http-Header=Content-Type: text/html; charset=utf-8
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
aber hier haben wir dann ä ich verstehe das nicht...

  1. Hallo,

    typisches Sonderzeichen Problem, ein Ä wird zum ä. Das Ä kommt aus einer Kommentarfunktion, die eigentlich absolut sauber ist, ich kann mir nicht erklären wie das Ä sich verwandeln kann.

    So wies aussieht, wird da ein Ä zunächst in eine kleines ä umgewandelt, das dann utf-8-kodiert abgespeichert und mit einer nicht-utf-8-kodierung (wahrscheinlich iso oder Windows-Codepage) dargestellt. Prüf das mal.

    Hotti

  2. Hi!

    typisches Sonderzeichen Problem, ein Ä wird zum ä. Das Ä kommt aus einer Kommentarfunktion, die eigentlich absolut sauber ist, ich kann mir nicht erklären wie das Ä sich verwandeln kann.

    Wenn "eigentlich alles sauber" ist und noch dazu absolut, dürftest du keinen Fehler haben. Programmieren ist keine "eigentlich"-Tätigkeit. Ein Computer (ohne Hardwaredefekt) arbeitet immer exakt, der Fehler steckt stets im Programm. Wenn nicht in deinem, dann in einem anderen.

    1 Aufruf des Kommentarformulares
    http-Header=Content-Type: text/html; charset=utf-8
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    Anhand der Einstellungen des Formulars wird der Browser die Antwort kodieren, obwohl die Angaben des aktuellen Dokuments nichts über die Fähigkeiten und Wünsche des zukünftigen Requestsziels (das des Formulars) weiß. Das accept-charset-Attribut eines Formulars wäre zwar eine Möglichkeit, aber das kann man getrost vergessen, weil es nicht browserübergreifend korrekt implementiert ist.

    2 Senden des Kommentarformulares
    http-Header=Content-Type: text/html; charset=utf-8
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    Das ist für die Verarbeitung komplett uninteressant, weil diese Angaben erst für den Browser von Interesse sind, wenn er die Antwort erhält.

    Wichtiger wär die Frage, was wirklich bei deinem Script ankommt. Lass dir das ausgeben, entweder mit bin2hex() oder urlencode()  aufbereitet (letzteres ist zwar nicht dafür vorgesehen, aber leichter lesbar).

    an Mysql auch hier ist alles utf8_gerenal_ci

    Definiere "alles". Es sind (wie schon so oft hier gesagt) zwei Dinge wesentlich: die Kodierung der einzelnen Felder und die auf deiner Verbindung ausgehandelte Kodierung. Wie sind diese beiden Werte eingestellt?

    4 Auslesen bzw. Anzeigen des Kommentares
    http-Header=Content-Type: text/html; charset=utf-8
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    aber hier haben wir dann ä ich verstehe das nicht...

    Auch hier ist wieder die Verbindungskodierung interessant. Und natürlich die Frage: Was kommt konkret aus dem DBMS? Kontrollausgabe!

    Lo!

    1. Hallo,

      dann will ich dem einmal gerecht werden

      Anhand der Einstellungen des Formulars wird der Browser die Antwort kodieren, obwohl die Angaben des aktuellen Dokuments nichts über die Fähigkeiten und Wünsche des zukünftigen Requestsziels (das des Formulars) weiß. Das accept-charset-Attribut eines Formulars wäre zwar eine Möglichkeit, aber das kann man getrost vergessen, weil es nicht browserübergreifend korrekt implementiert ist.

      Ok also nicht setzten, weil nicht in allen Browser funktioniert ( verstanden ).

      Das ist für die Verarbeitung komplett uninteressant, weil diese Angaben erst für den Browser von Interesse sind, wenn er die Antwort erhält.

      Wichtiger wär die Frage, was wirklich bei deinem Script ankommt. Lass dir das ausgeben, entweder mit bin2hex() oder urlencode()  aufbereitet (letzteres ist zwar nicht dafür vorgesehen, aber leichter lesbar).

      Eingabe im Forumular: Ü
      Hex ausgegeben: 264174696c64653b9c
      PHPMyadmin zeigt an: Ü

      an Mysql auch hier ist alles utf8_gerenal_ci

      Definiere "alles". Es sind (wie schon so oft hier gesagt) zwei Dinge wesentlich: die Kodierung der einzelnen Felder und die auf deiner Verbindung ausgehandelte Kodierung. Wie sind diese beiden Werte eingestellt?

      in meiner .htacces steht:
      AddDefaultCharset UTF-8

      1. Ich hatte noch vergessen, das die Tabellen, als auch die Spalten "utf8_general_ci" als Kollation haben.

      2. Hallo,

        Eingabe im Forumular: Ü
        Hex ausgegeben: 264174696c64653b9c

        also im Klartext: "&Atilde;" und das weder in UTF-8 noch in ISO-sonstwas definierte Zeichen 0x9C.
        Hier ist der Unfall also schon passiert.
        Verstümmelst du deine Eingabedaten etwa gleich am Scriptanfang mit htmlentities()? Wenn ja, wozu?

        So long,
         Martin

        --
        Ich stehe eigentlich gern früh auf.
        Außer morgens.
        1. Hallo Martin,

          Hier ist der Unfall also schon passiert.
          Verstümmelst du deine Eingabedaten etwa gleich am Scriptanfang mit htmlentities()? Wenn ja, wozu?

          Wie ich hier im Forum gelernt habe, traut man keiner Nutzereingabe, und um mich vor XSS zu schützten nutze ich htmlentities.
          Ohne wäre der Buchsatabe "c39c"

          1. Hallo,

            Verstümmelst du deine Eingabedaten etwa gleich am Scriptanfang mit htmlentities()? Wenn ja, wozu?
            Wie ich hier im Forum gelernt habe, traut man keiner Nutzereingabe

            ja, dieser Gedanke ist absolut richtig.

            und um mich vor XSS zu schützten nutze ich htmlentities.

            Das ist die falsche Funktion an der falschen Stelle.
            1. htmlentities() sollte, wenn überhaupt, dort angewendet werden, wo Daten *in* den HTML-Kontext überführt werden, also bei den Ausgabedaten des Scripts. Für die Überführung in andere Kontexte (z.B. SQL, URL) gibt es andere Spezialfunktionen.
            2. Dort wo die Funktion sinnvoll wäre, sollte man basser htmlspecialchars() verwenden. Das codiert nämlich nur die Zeichen, bei denen es nötig ist und lässt alle anderen Zeichen in Ruhe.

            Ohne wäre der Buchsatabe "c39c"

            Das wäre die korrekte Repräsentation des Buchstaben Ü in UTF-8.

            So long,
             Martin

            --
            Niemand lebt allein von seinen Träumen.
            Aber wer träumt, lebt noch.
            1. Der Fehler lag in der Kommunikation zwischen Datenbank und Server.

              Für alle anderen dich auch noch Probleme haben:
              if (!mysqli_set_charset($db, "utf8")) {
                 printf("Error loading character set utf8: %s\n", mysqli_error($db));
              }

              ist eure Lösung...

              1. Hi!

                Der Fehler lag in der Kommunikation zwischen Datenbank und Server.

                Da auch, ja.

                Für alle anderen dich auch noch Probleme haben:
                [...]
                ist eure Lösung...

                So einfach kann man das nicht sehen. Die Kommunikation zwischen Brwoser, Webserver, PHP und MySQL (und weiterer Datenquellen) ist ein komplexes Gebilde. Man muss immer genau schauen, zwischen welchen beiden Systemen gerade kommuniziert wird, dass beide Partner die zu verwendende Kodierung wissen oder mitgeteilt bekommen und speziell für diese Systeme die Daten kodieren und gegebenenfalls maskieren.

                Lo!

                1. Hallo

                  Der Fehler lag in der Kommunikation zwischen Datenbank und Server.

                  Da auch, ja.

                  Für alle anderen dich auch noch Probleme haben:
                  [...]
                  ist eure Lösung...

                  So einfach kann man das nicht sehen. Die Kommunikation zwischen Brwoser, Webserver, PHP und MySQL (und weiterer Datenquellen) ist ein komplexes Gebilde.

                  Hinzu kommt noch ein mögliches Problem. Wenn die Daten vor dem Speichern (z.B. in der DB) noch mit Stringfunktionen bearbeitet werden, muss dies bei UTF-8 kodierten Strings im Bedarfsfall mit den mb-Funktionen erfolgen. Und selbst damit kann man (wie ich letztens) auf die Schnauze fallen, wenn man nicht prüft, ob von PHP intern auch UTF-8 verwendet wird. Das kann man mit mb_internal_encoding prüfen und mit selbiger Funktion bei Bedarf auch erzwingen.

                  Tschö, Auge

                  --
                  Verschiedene Glocken läuteten in der Stadt, und jede von ihnen vertrat eine ganz persönliche Meinung darüber, wann es Mitternacht war.
                  Terry Pratchett, "Wachen! Wachen!"
                  Veranstaltungsdatenbank Vdb 0.3
          2. Hi!

            Verstümmelst du deine Eingabedaten etwa gleich am Scriptanfang mit htmlentities()? Wenn ja, wozu?
            Wie ich hier im Forum gelernt habe, traut man keiner Nutzereingabe, und um mich vor XSS zu schützten nutze ich htmlentities.
            Ohne wäre der Buchsatabe "c39c"

            Eine Benutzereingabe ist etwas anders als eine Ausgabe. Dazwischen musst du mit unverfälschten Rohdaten arbeiten, sonst geht mehr schief als du zu kitten versuchst. htmlentities() ist eine Funktion, um für den Kontext HTML Daten aufzubereiten (wobei htmlspecialchars() die bessere Lösung ist). Du willst aber erst einmal Daten verarbeiten und sie in Richtung MySQL weitergeben. Dass sie irgendwann später in eine HTML-Seite eingefügt werden sollen, interessiert an der Stelle noch nicht. Es ist ja noch nicht einmal gesagt, dass sie überhaupt / immer in Richtung HTML ausgegeben werden. Vorauseilende Vorbereitung wirft stets mehr Probleme auf, als damit beseitigt werden sollen. Mitunter fallen die diese Probleme nur nicht sofort auf. Siehe dazu zum Beispiel HTML in der Datenbank (und am besten auch den Rest des Artikels).

            Lo!

      3. Hi!

        Eingabe im Forumular: Ü
        Hex ausgegeben: 264174696c64653b9c

        Ein einzelnes Ü ist in keiner gebräuchlichen Kodierung so lang. Mal sehen, was du wirklich eingegeben hast (beziehungsweise, was bei PHP angekommen ist). Dazu nimmt man eine Kodiertabelle, beispielsweise für den Anfang ISO-8859-1:

        26 41 74 69 6c 64 65 3b 9c
        &  A  t  i  l  d  e  ;  œ

        Ach, herrjeh! Da kommt also ein Mischmasch aus HTML-Entity-Referenz und einem einzelnen Byte an. Hast du die Seite irgendwo online, denn so ein Fall ist mir noch nicht vorgekommen. - Du hast aber zwischen dem Eingang der Daten und der Kontrollausgabe keine weitere Verarbeitung dazwischen?

        Deine Baustelle ist also erst einmal hier, obwohl du auch noch Wissenslücken zur nachfolgenden Verarbeitung hast.

        an Mysql auch hier ist alles utf8_gerenal_ci
        Definiere "alles". Es sind (wie schon so oft hier gesagt) zwei Dinge wesentlich: die Kodierung der einzelnen Felder und die auf deiner Verbindung ausgehandelte Kodierung. Wie sind diese beiden Werte eingestellt?
        in meiner .htacces steht:
        AddDefaultCharset UTF-8

        In dem Zusammenhang nicht weiter interessant. Das beeinflusst nur die Ausgabe an den Browser, nicht aber die davor stattfindende Verarbeitung in PHP. PHP spricht ja mit MySQL, also sind die Werte auf der Verbindung zwischen PHP und MySQL maßgebend. Stichwörter: mysql(i)_set_charset() oder ein SET NAMES Statement)

        Ich hatte noch vergessen, das die Tabellen, als auch die Spalten "utf8_general_ci" als Kollation haben.

        Wie gesagt, die Feldkodierung ist für die Ablage der Daten maßgebend, nicht die der Tabelle oder der Datenbank oder gar des Systems, denn das sind alles nur Default-Werte für nicht explizit angegebene Kodierungen/Kollationen untergeordneter Elemente.

        Lo!

        1. Hallo Dedlfixx

          ah ok, den htmlenteties habe ich aufgrund der XSS Problematik (traue keiner Nutzerangabe) genutzt, ohne wäre das zeichen:
                c3 9c
          also  Ã  œ   richtig verstanden?

          In dem Zusammenhang nicht weiter interessant. Das beeinflusst nur die Ausgabe an den Browser, nicht aber die davor stattfindende Verarbeitung in PHP. PHP spricht ja mit MySQL, also sind die Werte auf der Verbindung zwischen PHP und MySQL maßgebend. Stichwörter: mysql(i)_set_charset() oder ein SET NAMES Statement)

          Das ist garnicht gesetzt, ich werde jetzt aber nachbessern.

          1. Hi!

            ah ok, den htmlenteties habe ich aufgrund der XSS Problematik [...] genutzt, ohne wäre das zeichen:
                  c3 9c
            also  Ã  œ   richtig verstanden?

            Damit (und wenn du die MySQL-Verbindungskodierung noch einstellst) dürfte sich dein Problem gelöst haben.

            (traue keiner Nutzerangabe)

            Ja, aber es heißt nicht: verfälsche sie deswegen. :-)

            Es ist richtig, dass in Benutzereingaben alles mögliche drin stehen kann. Du solltest sie aber mit Bedacht behandeln.

            In einigen Fällen hat man einen klar umrissenen Wertebereich, den man erwartet, beispielsweise eine Zahl zwischen x und y oder einen Wert aus einer Liste vorgegebener Werte oder die Mindest- und/oder Maximal-Länge eines String. Gegen diesen Wertebereich kann man Eingabewerte exakt prüfen.

            Manchmal hat man aber nur grobe Vorstellungen: "der Wert muss eine Integerzahl sein", dann reicht es oft, diesen Typ zu erzwingen. intval() liefert garantiert eine Zahl, und Zweifelsfall ist das 0.

            Und dann gibt es Werte, die kann man nicht sinnvoll einschränken, den Inhalt eines frei formulierbaren Textes zum Beispiel. Einem solchen kann man mit technischen Mitteln nicht vollständig die unerwünschten Teile entfernen. Spam bleibt Spam, auch wenn man daraus den HTML-Teil gelöscht hat. In einem Gästebuch oder ähnlichen System stört er mit und ohne sichtbarem Code. Man kann nicht (gänzlich) verhindern, dass Müll ins System gelangt, man kann aber dafür zu sorgen, dass diese Texte keinen Schaden anrichten können. Dazu muss das empfangende System genau zwischen Daten und Codebestandteilen unterscheiden können. Alles was Daten ist, muss speziell für den Empfänger behandelt werden, egal aus welcher Quelle sie eben gerade oder irgendwann einmal gekommen sind.

            Aber, wie gesagt, erst zur Ausgabe darf behandelt werden, denn während der Verarbeitung stören die für die Maskierung zusätzlich eingefügten Zeichen. ("H&uuml;hner" besteht aus 11 Zeichen aber "Hühner" aus 6, "Hühner" sind nicht gleich "H&uuml;hner", "H&uuml;hner" werden vor "Hasen" einsortiert, obwohl ein ü im Alphabet hinter a kommt, und so weiter).

            Lo!

      4. hi,

        in meiner .htacces steht:
        AddDefaultCharset UTF-8

        Davon lässt sich PHP nicht beeindrucken. w3.org empfiehlt, zur Festlegung der Kodierung die Funktion header() zu benutzen.

        Hotti

        1. Hi!

          in meiner .htacces steht:
          AddDefaultCharset UTF-8
          Davon lässt sich PHP nicht beeindrucken. w3.org empfiehlt, zur Festlegung der Kodierung die Funktion header() zu benutzen.

          Auch davon lässt sich PHP nicht beeindrucken, genauso wie sich ein Briefumschlag nicht beeindrucken lässt, wenn du "100€" drauf schreibst.

          Der Apache jedenfalls reagiert auf diese Direktive auch bei PHP-Scripts (mit ohne header()) wie vorgesehen, solange in PHP nicht der Wert für default_mimetype geändert wird. (Die verlinkte Stelle schweigt zwar still, aber weiter oben in der Tabelle ist der Default-Wert "text/html" zu sehen.)

          Lo!

          1. hi,

            [..] aber weiter oben in der Tabelle ist der Default-Wert "text/html" zu sehen.)

            Nur mal so nebenbei: Zur Angabe des Content-Type gehört die Angabe charset. Das ist im HTTP-Header _ein_ Feld.

            Beispiel:
            Content-type: text/html; charset=UTF-8

            Viele Grüße,
            Horst Suppenhuhn

            --
            Eini richtiger Profi nimmt den Hammer in beide Hände.
            1. Hi!

              [..] aber weiter oben in der Tabelle ist der Default-Wert "text/html" zu sehen.)
              Nur mal so nebenbei: Zur Angabe des Content-Type gehört die Angabe charset. Das ist im HTTP-Header _ein_ Feld.
              Beispiel:
              Content-type: text/html; charset=UTF-8

              Ja, aber PHP trennt diese beiden Angaben in unterschiedliche Konfigurationswerte. Und, während default_mimetype den Default-Wert text/html hat, ist default_charset leer.

              Um beim Client beide Werte ankommen zu lassen, kann man entweder default_charset mit einem passenden Wert füllen (was aber ausprobierterweise nicht mehr mit ini_set() im Script geht, obwohl default_charset PHP_INI_ALL ist. Man muss es schon davor setzen, spätestens mit php_value in der .htaccess, wenn PHP als Apache-Modul läuft.) Eine Alternative ist AddDefaultCharset in der Apache-Konfiguration oder eine explizite header()-Sendung seitens PHP.

              Lo!

              1. Hi!

                Content-type: text/html; charset=UTF-8

                Ja, aber PHP trennt diese beiden Angaben in unterschiedliche Konfigurationswerte. Und, während default_mimetype den Default-Wert text/html hat, ist default_charset leer.

                Früher hab ich das auch immer leer gelassen. Das ist nicht empfehlenswert.

                [..] Eine Alternative ist AddDefaultCharset in der Apache-Konfiguration oder eine explizite header()-Sendung seitens PHP.

                Als Alternative sehe ich das nicht. Siehe auch:

                http://www.w3.org/International/O-HTTP-charset

                ".. Use the header() function before generating any content, .."

                -----> Und genau hier schlagen die üblichen Verdächtigen ein, die PHP dazu bringen, einen spontanen Header zu feuern, wo die charset Angabe auch noch fehlt.

                Viele Grüße,
                Horst Haselhuhn

                1. Hi!

                  Content-type: text/html; charset=UTF-8
                  Ja, aber PHP trennt diese beiden Angaben in unterschiedliche Konfigurationswerte. Und, während default_mimetype den Default-Wert text/html hat, ist default_charset leer.
                  Früher hab ich das auch immer leer gelassen. Das ist nicht empfehlenswert.

                  Du verwechselst da grad was. Ich sprach nicht von der Angabe im HTTP-Header sondern von der PHP-Direktive.

                  [..] Eine Alternative ist AddDefaultCharset in der Apache-Konfiguration oder eine explizite header()-Sendung seitens PHP.
                  Als Alternative sehe ich das nicht. Siehe auch:

                  Fehlinterpretation als Folgefehler deinerseits.

                  http://www.w3.org/International/O-HTTP-charset
                  ".. Use the header() function before generating any content, .."

                  -----> Und genau hier schlagen die üblichen Verdächtigen ein, die PHP dazu bringen, einen spontanen Header zu feuern, wo die charset Angabe auch noch fehlt.

                  PHP feuert keine spontanen Header. Entweder man setzt welche mit header(), oder PHP teilt dem Apachen den Wert unter default_mimetype mit, welcher Content-type zu erwarten ist (und, falls gefüllt, default_charset). Ob der Apache in dem Fall, in dem ihm keine charset-Angabe vorliegt, eine solche ergänzt, ist in seiner Konfiguration festgelegt.

                  Dass es sinnvoll ist, eine charset-Angabe in Richtung Client zu senden, steht außer Frage. Hier geht es aber darum, wie man das hinbekommt, und dass es mehrere Wege gibt, das zu erreichen.

                  Lo!

                  1. hi,

                    Dass es sinnvoll ist, eine charset-Angabe in Richtung Client zu senden, steht außer Frage.

                    Achso, ja klar.

                    Hier geht es aber darum, wie man das hinbekommt, und dass es mehrere Wege gibt, das zu erreichen.

                    Auch klar. Einen davon habe ich beschrieben.

                    Viele Grüße,
                    Horst

            2. Hi,

              Nur mal so nebenbei: Zur Angabe des Content-Type gehört die Angabe charset.

              Ach ja?
              Welches Charset hat denn bspw. eine Ressource vom Typ image/png?

              MfG ChrisB

              --
              “Whoever best describes the problem is the person most likely to solve the problem.” [Dan Roam]