Unterschiedlicher MD5-Hash bei Insert und Update

Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 13:01:52 von Michel Fouquet

Hallo Leute,

unter folgender Konfiguration bin ich auf ein Problem gestoßen
MySQL-Client-Version: 5.0.41-community-nt
phpMyAdmin 2.10.1

Ich möchte eine Tabelle mit einer Liste MD5-verschlüsselter Begriffe =

(z.B. Namen) füttern, in denen Umlaute vorkommen können.

Über PMA setze ich folgende SQL-Statements ab:

INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES (md5('gut'),'gut=
');
INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES=20
(md5('böse'),'böse');

dies ergibt die Hashwerte

bd4ca3a838ce3972af46b6e2d85985f2
ece40577fee0833e87ee83a4c168f6f7

Zum Gegenschecken habe ich folgende Statements abgesetzt:

INSERT INTO `umlauttest` (`klartext`) VALUES ('gut');
INSERT INTO `umlauttest` (`klartext`) VALUES ('böse');

UPDATE `umlauttest` SET `hashwert` =3D md5(klartext) WHERE id=3D3;
UPDATE `umlauttest` SET `hashwert` =3D md5(klartext) WHERE id=3D4;

dies ergibt die Hashwerte

bd4ca3a838ce3972af46b6e2d85985f2
e4eb1f1bcbb7e87ef4f928d67ea9eb77

Dabei stellt sich das Problem, je nachdem, ob ein direkter Insert oder=20
ein Update abgesetzt wurde, dass sich die Hashwerte unterscheiden, wenn=20
der Klartext Umlaute enthält.

Richtig in o.a. Beispiel wäre der Hash e4eb1f1bcbb7e87ef4f928d67ea9eb77=
,=20
der sich auch über ein Insert per PHP ergäbe.

Kann jemand diese Problematik nachvollziehen?
Hat jemand eine Erklärung dafür, warum obige zwei SQL-Statements zu=20
unterschiedlichen Ergebnissen führen?

mfg,
Michel

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 13:32:48 von Christian Kirsch

Am 04.07.2007 13:01 schrieb Michel Fouquet:

> Hallo Leute,
>
> unter folgender Konfiguration bin ich auf ein Problem gestoßen
> MySQL-Client-Version: 5.0.41-community-nt
> phpMyAdmin 2.10.1
>


Vergiss PHPMyAdmin



Dein Beispiel ist nur relevant, wenn es mit mysql (dem
Kommandozeilen-Client) reproduzierbar ist.

> Ich möchte eine Tabelle mit einer Liste MD5-verschlüsselter Begriffe
> (z.B. Namen) füttern, in denen Umlaute vorkommen können.
>
> Über PMA setze ich folgende SQL-Statements ab:
>
> INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES (md5('gut'),'gut');
> INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES
> (md5('böse'),'böse');
>
> dies ergibt die Hashwerte
>
> bd4ca3a838ce3972af46b6e2d85985f2
> ece40577fee0833e87ee83a4c168f6f7
>

Dito mit Perls Digest::MD5. Allerdings bringen sowohl md5sum als auch
openssl dgst -md5 *andere* Ergebnisse!

ck@held:~> perl -MDigest::MD5
use Digest::MD5 qw(md5_hex);
print md5_hex('gut'),"\n";
print md5_hex('böse'),"\n";
bd4ca3a838ce3972af46b6e2d85985f2
ece40577fee0833e87ee83a4c168f6f7

ck@held:~> echo "gut" | openssl dgst -md5
99e98664b54c7f8ba71a99a96c51ae47
ck@held:~> echo "böse" | openssl dgst -md5
26806f5cf355dd78bbe9a156db5501d4

ck@held:~> echo "gut" | md5sum
99e98664b54c7f8ba71a99a96c51ae47 -
ck@held:~> echo "böse" | md5sum
26806f5cf355dd78bbe9a156db5501d4 -

Beim leeren String ('') sind sich MySQL und Perl einig, md5sum kommt
wieder auf was anderes:

ck@held:~> mysql bla -e "select md5('')"
+----------------------------------+
| md5('') |
+----------------------------------+
| d41d8cd98f00b204e9800998ecf8427e |
+----------------------------------+

ck@held:~> perl -MDigest::MD5
use Digest::MD5 qw(md5_hex);
print md5_hex(''),"\n";
d41d8cd98f00b204e9800998ecf8427e
ck@held:~> echo '' | md5sum
68b329da9893e34099c7d8ad5cb9c940 -

> Zum Gegenschecken habe ich folgende Statements abgesetzt:
>
> INSERT INTO `umlauttest` (`klartext`) VALUES ('gut');
> INSERT INTO `umlauttest` (`klartext`) VALUES ('böse');
>
> UPDATE `umlauttest` SET `hashwert` = md5(klartext) WHERE id=3;
> UPDATE `umlauttest` SET `hashwert` = md5(klartext) WHERE id=4;
>
> dies ergibt die Hashwerte
>
> bd4ca3a838ce3972af46b6e2d85985f2
> e4eb1f1bcbb7e87ef4f928d67ea9eb77
>
Das scheint auf ein Ümläut-Problem hinzudeuten. Noch ein Grund mehr,
es auf der Kommandozeile auszuprobieren und sämtliche
Charset-Einstellungen zu kontrollieren.


> Dabei stellt sich das Problem, je nachdem, ob ein direkter Insert oder
> ein Update abgesetzt wurde, dass sich die Hashwerte unterscheiden, wenn
> der Klartext Umlaute enthält.
>
> Richtig in o.a. Beispiel wäre der Hash e4eb1f1bcbb7e87ef4f928d67ea9eb77,

das allerdings sieht Perl anders - das optiert für ece405...

> der sich auch über ein Insert per PHP ergäbe.
>
M.E. geht es hier um zwei Fragen: Warum haben md5sum/openssl
einerseits und MySQL/Perl andererseits so unterschiedliche Ideen von
MD5? Und natürlich "Warum scheint derselbe String innerhalb von MySQL
zwei verschiedene MD5-Werte zu liefern".

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 13:35:45 von Michael Ziegler

Michel Fouquet wrote:
> Hat jemand eine Erklärung dafür, warum obige zwei SQL-Statements zu
> unterschiedlichen Ergebnissen führen?

Spontan würde ich jetzt mal raten, dass der Umlaut in "böse" Probleme macht.
Bist du sicher, dass der Umlaut richtig gespeichert wurde? Will sagen,
ist der Inhalt der 'Klartext'-Spalte in dem Zeichensatz codiert, der in
der Tabellendefinition angegeben ist?

Gruß,
Michael

--
Testscript für RegEchsen:
http://diesundas.funzt-halt.net/regextest.php

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 13:36:49 von Christian Kirsch

Am 04.07.2007 13:32 schrieb Christian Kirsch:
> Am 04.07.2007 13:01 schrieb Michel Fouquet:
>
....
>> INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES (md5('gut'),'gut');
>> INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES
>> (md5('böse'),'böse');
>>
>> dies ergibt die Hashwerte
>>
>> bd4ca3a838ce3972af46b6e2d85985f2
>> ece40577fee0833e87ee83a4c168f6f7
>>

Nachtrag: http://hashkiller.com/password/ liefert exakt dasselbe.

--
Christian

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 13:41:34 von Christian Kirsch

Am 04.07.2007 13:01 schrieb Michel Fouquet:

> Über PMA setze ich folgende SQL-Statements ab:
>
> INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES (md5('gut'),'gut');
> INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES
> (md5('böse'),'böse');
>
> dies ergibt die Hashwerte
>
> bd4ca3a838ce3972af46b6e2d85985f2
> ece40577fee0833e87ee83a4c168f6f7
>

Tut mir Leid, wenn ich hier den Eindruck erwecke, in Selbstgespräche
zu verfallen, aber...


http://hashkiller.com/password/b%F6se (das ist "böse" in latin1)

liefert

e4eb1f1bcbb7e87ef4f928d67ea9eb77

während

http://hashkiller.com/password/b%C3%B6se (das ist "böse" in latin1)

auf
ece40577fee0833e87ee83a4c168f6f7

kommt.

Mit anderen Worten: ece... ist das, was Du für UTF-8 bekommst, e4e...
das Latin1-Äquivalent.

Jetzt darfst Du Dir überlegen, warum Du MySQL mal UTF8 und mal Latin1
schickst :-)
--
Christian

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 14:13:54 von Sven Paulus

Christian Kirsch wrote:
> ck@held:~> echo "gut" | openssl dgst -md5
> 99e98664b54c7f8ba71a99a96c51ae47
> ck@held:~> echo "böse" | openssl dgst -md5
> 26806f5cf355dd78bbe9a156db5501d4

> ck@held:~> echo "gut" | md5sum
> 99e98664b54c7f8ba71a99a96c51ae47 -
> ck@held:~> echo "böse" | md5sum
> 26806f5cf355dd78bbe9a156db5501d4 -

Hier hast Du jeweils ein abschliessendes LF mitgehasht. "echo -n" ist Dein
Freund.

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 14:21:41 von Christian Kirsch

Am 04.07.2007 14:13 schrieb Sven Paulus:
> Christian Kirsch wrote:
>> ck@held:~> echo "gut" | openssl dgst -md5
>> 99e98664b54c7f8ba71a99a96c51ae47
>> ck@held:~> echo "böse" | openssl dgst -md5
>> 26806f5cf355dd78bbe9a156db5501d4
>
>> ck@held:~> echo "gut" | md5sum
>> 99e98664b54c7f8ba71a99a96c51ae47 -
>> ck@held:~> echo "böse" | md5sum
>> 26806f5cf355dd78bbe9a156db5501d4 -
>
> Hier hast Du jeweils ein abschliessendes LF mitgehasht. "echo -n" ist Dein
> Freund.

Argh. Wie peinlich.

--
Christian

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 16:24:14 von Michel Fouquet

Hallo,

Christian Kirsch schrieb:

> Dein Beispiel ist nur relevant, wenn es mit mysql (dem
> Kommandozeilen-Client) reproduzierbar ist.

naja, für mich hat das Beispiel auch so eine praktische Relevanz, weil =

ich ja nur mit den mir bekannten Instrumenten arbeiten kann. Ich möchte=
=20
mir nämlich an meinem Heimarbeitsplatz über das bei dir unbeliebte PM=
A=20
ein SQL-Skript generieren lassen, das ich dann - ebenfalls über PMA -=20
auf dem Server meines ISPs ausführen lassen möchte.

Ich möchte meine Tabelle ja nicht über 500 Kommandozeilen-Befehle=20
befüllen oder über Einzeleingaben eines Formulars.

(In der Entwicklungsphase ist in der Tabelle zu Kontrollzwecken halt=20
noch das Klartext-Feld drin - zum Glück, wie meine Beobachtungen zeigen=
.
;-) Ich komme aus der Access-Welt und mache hier nur einen kurzen=20
Ausflug in die MySQL/PHP-Welt).

> Dito mit Perls Digest::MD5. Allerdings bringen sowohl md5sum als auch
> openssl dgst -md5 *andere* Ergebnisse!

[snip]

Von perl und openssl habe ich keine Ahnung, die gehen mich in diesem=20
Zusammenhang vermutlich auch nichts an. Mich interessiert nicht, ob es=20
möglicherweise unterschiedliche Implementierungen eines Algorithmus gib=
t.

[snip]

> Das scheint auf ein Ümläut-Problem hinzudeuten. Noch ein Grund mehr=
,
> es auf der Kommandozeile auszuprobieren und sämtliche
> Charset-Einstellungen zu kontrollieren.

Ja, dass es nur bei Umlauten auftritt, schrieb ich im OP:

>> Dabei stellt sich das Problem, je nachdem, ob ein direkter Insert oder=
=20
>> ein Update abgesetzt wurde, dass sich die Hashwerte unterscheiden, wen=
n=20
>> der Klartext Umlaute enthält.

Nochmal: ich fülle dasselbe Feld derselben Tabelle in derselben=20
Datenbank über dieselbe Schnittstelle mit Inhalt - einmal per Insert,=20
einmal per Update. Ich habe keinerlei händische Intervention dabei,=20
unterschiedliche Schreibweisen mit oder ohne line feed oder Tabulator=20
scheiden aus. Ich schicke den Inhalt ja auch nicht über ein Formular=20
einer exotisch codierten Website. (Oder doch?)


Jetzt darfst Du Dir überlegen, warum Du MySQL mal UTF8 und mal Latin1
schickst


Mit dem Überlegen war ich halt nicht weitergekommen, deswegen habe ich =

mich ja an die NG gewendet. Als platter Empirist bin ich dann Deinem und =

Michaels Hinweis nachgegangen und habe so die Lösung gefunden.

In PMA - ja, ja! - findet sich auf der Startseite die=20
Einstellmöglichkeit "Zeichensatz/Kollation der MySQL-Verbindung". Dort =

war "utf8_unicode_ci" eingetragen, die Tabelle selber und das=20
Tabellenfeld war aber mit "latin1_german1_ci" definiert. Beim Insert=20
("von aussen") wurde also wohl utf8 verwendet, beim (tabelleninternen)=20
Update hingegen latin1.

Nach dem Umstellen der Verbindung auf latin1 haben die beiden Einträge =

der umlautigen Klartexte jetzt auch denselben Hash, und zwar auch den,=20
den ich über PHP bekommen hätte.

In einer ruhigen Minute versuche ich dann auch noch, beide Einstellungen =

(Verbindung und Tabelle) auf utf8 zu setzen und zu prüfen, wie das=20
Ergebnis dann aussieht.

Danke jedenfalls für den Anstoß.

mfg,
Michel

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 04.07.2007 16:24:18 von Michel Fouquet

Hallo,

Michael Ziegler schrieb:
> Michel Fouquet wrote:
>> Hat jemand eine Erklärung dafür, warum obige zwei SQL-Statem=
ents zu=20
>> unterschiedlichen Ergebnissen führen?
>=20
> Spontan würde ich jetzt mal raten, dass der Umlaut in "böse" =
Probleme=20
> macht.

das Raten war ja nicht schwer, weil ich das ja im OP schon selber=20
geschrieben habe.

> Bist du sicher, dass der Umlaut richtig gespeichert wurde? Will sagen, =

> ist der Inhalt der 'Klartext'-Spalte in dem Zeichensatz codiert, der in=
=20
> der Tabellendefinition angegeben ist?

Ist mir nicht so ganz klar und sicher bin ich mir da auch nicht.

Ich führe die im OP angegebenen SQL-Statements hintereinander aus, m=
it=20
gleiche Schreibweise der Inhalte. Ich komme also nicht über eine=20
exotisch codierte Website oder irgendeine, von mir sowieso nicht=20
verstandene, umcodierte PHP-Einstellung.

Allerdings sind die Hinweise von Christian und Dir trotzdem zielführ=
end=20
gewesen! Ich komme ja über PMA - und in PMA findet sich auf der=20
Startseite die Einstellmöglichkeit "Zeichensatz/Kollation der=20
MySQL-Verbindung". Dort war "utf8_unicode_ci" eingetragen, die Tabelle=20
selber und das Tabellenfeld war aber mit "latin1_german1_ci" definiert.

Beim Insert ("von aussen") wurde also wohl utf8 verwendet, beim=20
(tabelleninternen) Update hingegen latin1.

Durch Umstellen des Verbindungszeichensatzes ebenfalls auf latin1 und=20
erneutes Ausführen der SQL-Statements war mein Problem dann gelö=
st.

Danke!

mfg,
Michel

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 06.07.2007 00:39:11 von Axel Schwenke

Christian Kirsch wrote:
>
> Tut mir Leid, wenn ich hier den Eindruck erwecke, in Selbstgespräche
> zu verfallen, aber...
>
> http://hashkiller.com/password/b%F6se (das ist "böse" in latin1)
> liefert
> e4eb1f1bcbb7e87ef4f928d67ea9eb77
>
> während
> http://hashkiller.com/password/b%C3%B6se (das ist "böse" in latin1)
> auf
> ece40577fee0833e87ee83a4c168f6f7
> kommt.
>
> Mit anderen Worten: ece... ist das, was Du für UTF-8 bekommst, e4e...
> das Latin1-Äquivalent.
>
> Jetzt darfst Du Dir überlegen, warum Du MySQL mal UTF8 und mal Latin1
> schickst :-)

Das ist jetzt ganz leicht aufzuklären. Bei

INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES
(md5('böse'),'böse');

wird der MD5-Hash von einem String-Literal gebildet. Bei

INSERT INTO `umlauttest` (`klartext`) VALUES ('böse');
UPDATE `umlauttest` SET `hashwert` = md5(klartext) WHERE id=4;

wird hingegen der String aus der Spalte `klartext` verwendet.
Offensichtlich verwendet der Client (PMA) als Encoding utf8,
während die Spalte `klartext` als latin1 deklariert ist. MySQL
wandelt den Umlaut also zur Speicherung von utf8 nach latin1.

Die MD5-Funktion behandelt das Argument aber als BINARY String.
D.h. als eine Folge von Bytes ohne Encoding-Information. Sowohl
utf8 als auch latin1 kann MySQL in BINARY umwandeln. Nur passiert
da eben genau das, was man oben sehen kann, aus _latin1'böse'
wird [62 F6 73 65] und aus _utf8'böse' wird [62 C3 B6 73 65].
Damit sind das zwei verschiedene Strings und MD5() berechnet
folgerichtig verschiedene Hashwerte.

Für das gewünschte Verhalten müßte man das UPDATE so schreiben:
UPDATE umlauttest SET hashwert = MD5(CONVERT(klartext USING utf8))


XL

Re: Unterschiedlicher MD5-Hash bei Insert und Update

am 06.07.2007 14:34:49 von Michel Fouquet

Hallo Axel,

Axel Schwenke schrieb:

> Das ist jetzt ganz leicht aufzuklären. Bei
>=20
> INSERT INTO `umlauttest` (`hashwert`, `klartext`) VALUES
> (md5('böse'),'böse');
>=20
> wird der MD5-Hash von einem String-Literal gebildet. Bei
>=20
> INSERT INTO `umlauttest` (`klartext`) VALUES ('böse');
> UPDATE `umlauttest` SET `hashwert` =3D md5(klartext) WHERE id=3D4;
>=20
> wird hingegen der String aus der Spalte `klartext` verwendet.
> Offensichtlich verwendet der Client (PMA) als Encoding utf8,
> während die Spalte `klartext` als latin1 deklariert ist. MySQL
> wandelt den Umlaut also zur Speicherung von utf8 nach latin1.
>=20
> Die MD5-Funktion behandelt das Argument aber als BINARY String.
> D.h. als eine Folge von Bytes ohne Encoding-Information. Sowohl
> utf8 als auch latin1 kann MySQL in BINARY umwandeln. Nur passiert
> da eben genau das, was man oben sehen kann, aus _latin1'böse'
> wird [62 F6 73 65] und aus _utf8'böse' wird [62 C3 B6 73 65].
> Damit sind das zwei verschiedene Strings und MD5() berechnet
> folgerichtig verschiedene Hashwerte.
>=20
> Für das gewünschte Verhalten müßte man das UPDATE so schreiben:=

> UPDATE umlauttest SET hashwert =3D MD5(CONVERT(klartext USING utf8))

wie der Hesse sagt: "wer's waas werd's wisse!" Ja, wenn man's weiss, ist =

es dann "ganz leicht aufzuklären" und "offensichtlich".

Obwohl für mich die Sache bereits abgehakt war: deine einfachen, klaren=
=20
Ausführungen haben es mir nun erst verständlich gemacht. (Meine "v=
on=20
aussen / von innen"-Eselsbrücke behalte ich im Kopf allerdings bei).

mfg,
Michel