Discussion:
Eigenes java.lang.String verwenden
(zu alt für eine Antwort)
Lothar Kimmeringer
2012-12-29 21:44:19 UTC
Permalink
Hallo,

habe mir gerade den Vortrag zum Thema Hashflooding reloaded
angesehen und habe wieder einen Hals...[1]

Da in der Behebung des Bugs Oracle anscheinend die gleiche
GFY-Haltung an den Tag zu legen scheint, moechte ich das bis
zum Fix in die eigenen Haende nehmen.

Gibt es eine Moeglichkeit fuer Java 1.6 und groesser, ohne
Anpassung der verwendeten VM (JDK bzw. JRE) java.lang.String
durch eine eigene Klasse zu ersetzen?


[1]
http://events.ccc.de/congress/2012/Fahrplan/events/5152.en.html
Das ist die Fortsetzung von
http://events.ccc.de/congress/2011/Fahrplan/events/4680.en.html
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Florian Weimer
2012-12-29 21:51:44 UTC
Permalink
Post by Lothar Kimmeringer
Gibt es eine Moeglichkeit fuer Java 1.6 und groesser, ohne
Anpassung der verwendeten VM (JDK bzw. JRE) java.lang.String
durch eine eigene Klasse zu ersetzen?
Mit Bytecode-Rewriting geht das in jedem Fall.

Davon abgesehen unterstützt ein aktueller Hotspot doch Strings mit
zwei unterschiedlichen Hash-Werten (einen für die
Rückwärtskompatibilität, und einen, der nicht vorhersehbar sein sollte
und ggf. einfach gepatcht werden kann).
Lothar Kimmeringer
2012-12-29 23:42:32 UTC
Permalink
Post by Florian Weimer
Post by Lothar Kimmeringer
Gibt es eine Moeglichkeit fuer Java 1.6 und groesser, ohne
Anpassung der verwendeten VM (JDK bzw. JRE) java.lang.String
durch eine eigene Klasse zu ersetzen?
Mit Bytecode-Rewriting geht das in jedem Fall.
Genau das wuerde ich gerne vermeiden, ich dachte da eher an
etwas wie eine eigene Klasse im Classpath vor die offizielle legen.

Was ich noch "akzeptieren" koennte, waere eine Loesung mittels
Reflection, nur da faellt mir so spontan auch nichts ein (koennte
aber auch an der Zeit liegen).
Post by Florian Weimer
Davon abgesehen unterstützt ein aktueller Hotspot doch Strings mit
zwei unterschiedlichen Hash-Werten (einen für die
Rückwärtskompatibilität, und einen, der nicht vorhersehbar sein sollte
und ggf. einfach gepatcht werden kann).
Schau dir den Vortrag an [1]. Der verwendete MurmurHash-Algorithmus
mit randomisiertem Seed kann recht einfach umgangen werden, da der
Seed bei geeigneten Eingangsdaten gar keine Rolle spielt.

Laut http://www.ocert.org/advisories/ocert-2012-001.html ist die
neue (das Problem nicht loesende) Funktion noch dazu per default
abgeschaltet. Beide Funktionen fuehren dazu, dass man mit einem
Netbook, die Webserver einer kompletten Farm lahmlegen kann.
... oder jede andere Applikation, die Daten von Dritten als
Schluessel in einer HashMap verwendet.

Von daher bin ich nicht mehr bereit, auf einen Fix von Oracles
Gnaden zu warten. Letztes Jahr habe ich selbst einen Bugreport
in die Bugparade eingestellt, der nach 12 Stunden geschlossen
wurde mit der Bemerkung, man moege doch bitte in der eigenen
Applikation statt HashMap auf TreeMap umsteigen...


Gruesse, Lothar

[1] ftp://mirror.fem-net.de/CCC/29C3/mp4-h264-HQ
Sollte dort am Sonntag im Laufe des Tages auftauchen
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Patrick Roemer
2012-12-30 11:52:54 UTC
Permalink
Post by Lothar Kimmeringer
Post by Florian Weimer
Post by Lothar Kimmeringer
Gibt es eine Moeglichkeit fuer Java 1.6 und groesser, ohne
Anpassung der verwendeten VM (JDK bzw. JRE) java.lang.String
durch eine eigene Klasse zu ersetzen?
Mit Bytecode-Rewriting geht das in jedem Fall.
Genau das wuerde ich gerne vermeiden, ich dachte da eher an
etwas wie eine eigene Klasse im Classpath vor die offizielle legen.
-Xbootclasspath/p:mystring.jar

...nur scheint es native Methoden zu geben, die gewisse Erwartungen an
die Hashcode-Implementierung haben - bei manchen (den meisten) naiven
Modifikationen, die ich gerade ausprobiert habe, pengt es mit einer NPE
aus System.initProperties(), bei einigen klappt es (zumindest weit genug
um ein paar Hashcodes auf die Konsole auszugeben).

Grundsaetzlich waere ich bei solchen Aktionen aber eher skeptisch: Wenn
man Sicherheitsprobleme fixen will, und nicht saemtliche Auswirkungen
der Bastelei zu 100% einschaetzen kann, sind die Chancen gut, dass man
dabei anderswo noch groessere Loecher reisst.

Und dann waere da noch:

| C. Java Technology Restrictions. You may not create, modify, or
| change the behavior of, or authorize your licensees to create,
| modify, or change the behavior of, classes, interfaces, or
| subpackages that are in any way identified as "java", "javax", "sun"
| or similar convention as specified by Sun in any naming convention
| designation.

Viele Gruesse,
Patrick
Lothar Kimmeringer
2012-12-30 13:10:19 UTC
Permalink
Post by Patrick Roemer
-Xbootclasspath/p:mystring.jar
...nur scheint es native Methoden zu geben, die gewisse Erwartungen an
die Hashcode-Implementierung haben
Wuerde mich wundern, ich behaupte mal, dass da irgendwas beim Class-
loading schief geht.
Post by Patrick Roemer
- bei manchen (den meisten) naiven
Modifikationen, die ich gerade ausprobiert habe, pengt es mit einer NPE
aus System.initProperties(),
NPEs sind so ein Zeichen fuer so was.

Aber das mit dem Boot-Classpath wollte ich mir sowieso anschauen,
vielleicht findet sich da ein Ansatzpunkt. Ansonsten halt ASM...
Post by Patrick Roemer
Grundsaetzlich waere ich bei solchen Aktionen aber eher skeptisch: Wenn
man Sicherheitsprobleme fixen will, und nicht saemtliche Auswirkungen
der Bastelei zu 100% einschaetzen kann, sind die Chancen gut, dass man
dabei anderswo noch groessere Loecher reisst.
Zum Spass mache ich sowas auch nicht. Das ist nur das Taegliche
Murmeltier. Auch unsere Kunden schauen den 29C3 bzw. verfolgen die
solche Vortraege ausloesende Berichterstattung, d.h. ich werde
naechste Woche diverse Anfragen bekommen, wie da das weitere Vor-
gehen sein wird. Mit der Erfahrung, die ich mit Oracle in der Ver-
gangenheit gemacht habe, bleibt einem bei sowas ja nicht viel
anderes als selbst fixen. Den Fix deaktivieren, wenn eine gefixte
JVM verwendet wird, kann man ja immer noch.

http://jcapi.cvs.sourceforge.net/viewvc/jcapi/net/sourceforge/jcapi/rcapi/
habe ich auch nicht zum Spass gemacht, schliesslich war dafuer eine
gute Woche Wireshark-Traces Lesen notwendig.
Post by Patrick Roemer
| C. Java Technology Restrictions. You may not create, modify, or
| change the behavior of, or authorize your licensees to create,
| modify, or change the behavior of, classes, interfaces, or
| subpackages that are in any way identified as "java", "javax", "sun"
| or similar convention as specified by Sun in any naming convention
| designation.
Die Behavior bleibt ja gleich. Hashcode liefert weiterhin einen Hashcode.
Und ausserdem: "Von Produktivgehen war nie die Rede" ;-) [1]


Gruesse, Lothar

[1] Originalspruch eines Consultants, nachdem die gesamte Projekt-
zeit mit einer Testinstallation verbraten wurde.
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Florian Weimer
2012-12-30 15:22:51 UTC
Permalink
Post by Patrick Roemer
| C. Java Technology Restrictions. You may not create, modify, or
| change the behavior of, or authorize your licensees to create,
| modify, or change the behavior of, classes, interfaces, or
| subpackages that are in any way identified as "java", "javax", "sun"
| or similar convention as specified by Sun in any naming convention
| designation.
Es gab diesen Text auch mal mit

| subpackages that are in any way identified as "java", "javax", "oracle"

, d.h. sun.misc.Hashing wäre nicht erfaßt.
Florian Weimer
2012-12-30 12:32:19 UTC
Permalink
Post by Lothar Kimmeringer
Post by Florian Weimer
Davon abgesehen unterstützt ein aktueller Hotspot doch Strings mit
zwei unterschiedlichen Hash-Werten (einen für die
Rückwärtskompatibilität, und einen, der nicht vorhersehbar sein sollte
und ggf. einfach gepatcht werden kann).
Schau dir den Vortrag an [1]. Der verwendete MurmurHash-Algorithmus
mit randomisiertem Seed kann recht einfach umgangen werden, da der
Seed bei geeigneten Eingangsdaten gar keine Rolle spielt.
Nun ja, das Video anzuschauen dauert ungefähr so lange, wie das
Schreiben von Java-Code für eine bessere Hash-Funktion (z.B. eines
Java-Ports von Jenkins' lookup3). Aber das Problem scheint mir gar
nicht so sehr die Hash-Funktion zu sein, sondern die Randomisierung an
sich (siehe unten).

sun.misc.Hashing kann sicherlich über den Boot-Classpath ersetzt
werden. java.lang.String muß gar nicht angefaßt werden, vermute ich.
Post by Lothar Kimmeringer
Laut http://www.ocert.org/advisories/ocert-2012-001.html ist die
neue (das Problem nicht loesende) Funktion noch dazu per default
abgeschaltet.
Nun ja, gerade in Testsuites gibt es häufig Abhängigkeiten von der
Reihenfolge, in der Hash-Tabellen ihre Wert liefern. Es ist blöd, wenn
plötzlich eine Menge anderer Software nicht mehr funktioniert und sich
z.B. Sicherheitsupdates nicht mehr kompilieren lassen, weil die
Testsuite nicht durchläuft.

Man hätte das sicherlich besser fixen können, indem man den sekundären
Hash nur bei vielen Kollisionen heranzieht (oder für die
kollidierenden Strings eine TreeMap, Patricia o.ä. nimmt, es gibt sehr
viele Möglichkeiten). Dadurch hätte man sich auch das hash32-Feld in
java.lang.String sparen können, weil viele Kollisionen eher selten
sind.

Danke für den Hinweis, mir war nicht klar, daß diese Baustelle noch
offen ist.
Post by Lothar Kimmeringer
Von daher bin ich nicht mehr bereit, auf einen Fix von Oracles
Gnaden zu warten.
OpenJDK gibt es auch noch aus anderen Quellen. 8-)
Lothar Kimmeringer
2012-12-30 13:33:42 UTC
Permalink
Post by Florian Weimer
Post by Lothar Kimmeringer
Schau dir den Vortrag an [1].
Nun ja, das Video anzuschauen dauert ungefähr so lange, wie das
Schreiben von Java-Code für eine bessere Hash-Funktion (z.B. eines
Java-Ports von Jenkins' lookup3).
Bei sowas kann man sich ruhig ein bisschen Zeit lassen. Lieber ein
wenig Grundwissen durch Anschauen von Vortraegen sammeln, als mal
schnell was zu implementieren, was am Ende auch nicht besser ist. Im
aktuellen Vortrag wird uebrigens ein Alternativhash (siphash) vorgestellt,
der auch schon in diversen betroffenen Produkten (Ruby, etc.) implementiert
wurde. Nur Oracle macht seit August nichts (da wurden sie benachrichtigt).
So wie halt schon letztes Jahr...
Post by Florian Weimer
Aber das Problem scheint mir gar
nicht so sehr die Hash-Funktion zu sein, sondern die Randomisierung an
sich (siehe unten).
Eine Hashfunktion, die auf simple Weise Kollisionen produziert,
taugt nicht.
Post by Florian Weimer
sun.misc.Hashing kann sicherlich über den Boot-Classpath ersetzt
werden. java.lang.String muß gar nicht angefaßt werden, vermute ich.
Das wird ja erst ab Java 7 verwendet, unsere Installationsbasis
ist aber groesstenteils noch Java 6.
Post by Florian Weimer
Post by Lothar Kimmeringer
Laut http://www.ocert.org/advisories/ocert-2012-001.html ist die
neue (das Problem nicht loesende) Funktion noch dazu per default
abgeschaltet.
Nun ja, gerade in Testsuites gibt es häufig Abhängigkeiten von der
Reihenfolge, in der Hash-Tabellen ihre Wert liefern.
Welche sind das? Nennen Namen, damit ich einen weiten Bogen um
solch daemlich programmierte Software machen kann. Der Hashcode
ist ja nicht die einzige Groesse, die die Reihenfolge bei einer
HashMap regelt, sondern auch die Groesse des internen Arrays, da
ja immer modulo Arraygroesse gerechnet wird. Das kann also auch
ohne neuer Hashfunktion zum Knall fuehren.

Ein reales Problem duerften folgende Konstrukte sein:

switch (mystring.hashcode()){
case 12345:{
if (mystring.equals("add")){
performAdd();
}
break;
}
...
}

Schaut komisch aus, ist aber performanter als ein
if (mystring.equals("add")){
}
else if (mystring.equals("remove"){
}
wenn die einzelnen Punkte gleich wahrscheinlich sind.

Wenn sich bei sowas unter der Haube die Hashcodeimplementierung
aendert, hat man natuerlich ein Problem. Das duerfte auch der
Grund sein, warum die neue (auch kaputte) Hashcodeimplementierung
defaultmaessig nicht aktiv ist.
Post by Florian Weimer
Man hätte das sicherlich besser fixen können, indem man den sekundären
Hash nur bei vielen Kollisionen heranzieht (oder für die
kollidierenden Strings eine TreeMap, Patricia o.ä. nimmt, es gibt sehr
viele Möglichkeiten).
Hashcodes auf String sind so eine zentrale Funktion, dass es keine
Loesung ist, die den Hashcode nutzenden Klassen einzeln an das
Problem anzupassen (hat man bei Ruby on Rails versucht, dabei aber
50% der Stellen uebersehen, wo das notwendig haette sein muessen).
Ich persoenlich halte es fuer besser, schlecht programmierte Software
auf die Nase fallen zu lassen. Das sind im Endeffekt weniger betroffene
Systeme als die, die beim Nichtfix auf einfachste Weise fuer DoS-
Angriffe anfaellig sind (nicht unbedingt von _u32 auf _u33, sondern
von Java x auf Java x+1).
Post by Florian Weimer
Dadurch hätte man sich auch das hash32-Feld in
java.lang.String sparen können, weil viele Kollisionen eher selten
sind.
Das hash-Feld in String dient doch AFIAK nur dem Caching, damit
man nicht staendig aufs neue kalkulieren muss (hash32 kenne ich
jetzt nicht so genau, bin noch primaer in Java 6 unterwegs, duerfte
aber aehnlich gedacht sein).
Post by Florian Weimer
Post by Lothar Kimmeringer
Von daher bin ich nicht mehr bereit, auf einen Fix von Oracles
Gnaden zu warten.
OpenJDK gibt es auch noch aus anderen Quellen. 8-)
Das Problem ist, dass ich nicht beliebig JVMs bei Kunden installieren
kann. Ich muss auch noch pruefen, inwieweit die IBM-VM betroffen ist,
da wir auch auf AIX und IBM i laufen. Und gerade auf AS/400-Systemen
ist die Auswahl an VMs recht uebersichtlich. ;-)


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Florian Weimer
2012-12-30 15:11:03 UTC
Permalink
Post by Lothar Kimmeringer
Bei sowas kann man sich ruhig ein bisschen Zeit lassen. Lieber ein
wenig Grundwissen durch Anschauen von Vortraegen sammeln, als mal
schnell was zu implementieren, was am Ende auch nicht besser ist.
Ein Video ist eine ziemlich ungeeignete Weise, solches Wissen zu
vermitteln.
Post by Lothar Kimmeringer
Post by Florian Weimer
sun.misc.Hashing kann sicherlich über den Boot-Classpath ersetzt
werden. java.lang.String muß gar nicht angefaßt werden, vermute ich.
Das wird ja erst ab Java 7 verwendet, unsere Installationsbasis
ist aber groesstenteils noch Java 6.
Holla. Dan sind Hash-Kollisionen aber Dein geringstes Problem.
Post by Lothar Kimmeringer
Post by Florian Weimer
Post by Lothar Kimmeringer
Laut http://www.ocert.org/advisories/ocert-2012-001.html ist die
neue (das Problem nicht loesende) Funktion noch dazu per default
abgeschaltet.
Nun ja, gerade in Testsuites gibt es häufig Abhängigkeiten von der
Reihenfolge, in der Hash-Tabellen ihre Wert liefern.
Welche sind das?
Stringtemplate z.B.
Post by Lothar Kimmeringer
switch (mystring.hashcode()){
case 12345:{
if (mystring.equals("add")){
performAdd();
}
break;
}
...
}
Deswegen ändert sich der Rückgabewert von hashCode() auch nicht.
(Implement javac nicht switch-on-string auf diese Weise?)
Post by Lothar Kimmeringer
Wenn sich bei sowas unter der Haube die Hashcodeimplementierung
aendert, hat man natuerlich ein Problem. Das duerfte auch der
Grund sein, warum die neue (auch kaputte) Hashcodeimplementierung
defaultmaessig nicht aktiv ist.
Nein, das ist nicht das Problem. Es ist wirklich die Reihenfolge bei
der Iteration. Die neuen Hashwerte sind nur auf diese Weise sichtbar.
Post by Lothar Kimmeringer
Post by Florian Weimer
Man hätte das sicherlich besser fixen können, indem man den sekundären
Hash nur bei vielen Kollisionen heranzieht (oder für die
kollidierenden Strings eine TreeMap, Patricia o.ä. nimmt, es gibt sehr
viele Möglichkeiten).
Hashcodes auf String sind so eine zentrale Funktion, dass es keine
Loesung ist, die den Hashcode nutzenden Klassen einzeln an das
Problem anzupassen (hat man bei Ruby on Rails versucht, dabei aber
50% der Stellen uebersehen, wo das notwendig haette sein muessen).
Bei Java gibt es keine andere Möglichkeit, weil der Wert von
hashCode() für Strings Teil der Spezifikation ist, siehe z.B. hier:

<http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/String.html#hashCode%28%29>
Post by Lothar Kimmeringer
Ich persoenlich halte es fuer besser, schlecht programmierte Software
auf die Nase fallen zu lassen. Das sind im Endeffekt weniger betroffene
Systeme als die, die beim Nichtfix auf einfachste Weise fuer DoS-
Angriffe anfaellig sind (nicht unbedingt von _u32 auf _u33, sondern
von Java x auf Java x+1).
Und genau das macht OpenJDK ja auch: wenn der Code aus nicht
vertrauenswürdiger Quelle größere Datenmengen akzeptiert, hat er
sicherlich auch andere Probleme. 8-)
Post by Lothar Kimmeringer
Post by Florian Weimer
Dadurch hätte man sich auch das hash32-Feld in
java.lang.String sparen können, weil viele Kollisionen eher selten
sind.
Das hash-Feld in String dient doch AFIAK nur dem Caching, damit
man nicht staendig aufs neue kalkulieren muss (hash32 kenne ich
jetzt nicht so genau, bin noch primaer in Java 6 unterwegs, duerfte
aber aehnlich gedacht sein).
Ja, das Cache-Feld ist aber bei der gegenwärtigen Implementierung
ziemlich wünschenswert, weil sonst get() oder containsKey() gefolgt
von put() schnell um einen Faktor zwei langsamer wird.
Lothar Kimmeringer
2012-12-30 15:39:09 UTC
Permalink
Post by Florian Weimer
Post by Lothar Kimmeringer
Bei sowas kann man sich ruhig ein bisschen Zeit lassen. Lieber ein
wenig Grundwissen durch Anschauen von Vortraegen sammeln, als mal
schnell was zu implementieren, was am Ende auch nicht besser ist.
Ein Video ist eine ziemlich ungeeignete Weise, solches Wissen zu
vermitteln.
Sondern?
Post by Florian Weimer
Post by Lothar Kimmeringer
Das wird ja erst ab Java 7 verwendet, unsere Installationsbasis
ist aber groesstenteils noch Java 6.
Holla. Dan sind Hash-Kollisionen aber Dein geringstes Problem.
Die da waeren?
Post by Florian Weimer
Post by Lothar Kimmeringer
switch (mystring.hashcode()){
case 12345:{
if (mystring.equals("add")){
performAdd();
}
break;
}
...
}
Deswegen ändert sich der Rückgabewert von hashCode() auch nicht.
Warum sollte bei geaendertem hashcode nicht ein anderer Rueckgabe-
wert kommen?
Post by Florian Weimer
(Implement javac nicht switch-on-string auf diese Weise?)
Vermutlich ja.
Post by Florian Weimer
Post by Lothar Kimmeringer
Hashcodes auf String sind so eine zentrale Funktion, dass es keine
Loesung ist, die den Hashcode nutzenden Klassen einzeln an das
Problem anzupassen (hat man bei Ruby on Rails versucht, dabei aber
50% der Stellen uebersehen, wo das notwendig haette sein muessen).
Bei Java gibt es keine andere Möglichkeit, weil der Wert von
hashCode() für Strings Teil der Spezifikation ist, siehe z.B. hier
Das war dann aber eine semischlaue Entscheidung.
Post by Florian Weimer
Post by Lothar Kimmeringer
Ich persoenlich halte es fuer besser, schlecht programmierte Software
auf die Nase fallen zu lassen. Das sind im Endeffekt weniger betroffene
Systeme als die, die beim Nichtfix auf einfachste Weise fuer DoS-
Angriffe anfaellig sind (nicht unbedingt von _u32 auf _u33, sondern
von Java x auf Java x+1).
Und genau das macht OpenJDK ja auch: wenn der Code aus nicht
vertrauenswürdiger Quelle größere Datenmengen akzeptiert, hat er
sicherlich auch andere Probleme. 8-)
Fakt ist: Der Aufwand bei einer durchschnittlichen Java-Applikation,
an allen Stellen, bei der man externe Daten in eine HashMap packt
(oder bei der auf andere Weise mit hashcode gearbeitet wird), ent-
sprechende Anpassungen einzubauen, duerfte durchaus vergleichbar
sein, das Teil auf eine andere plattformunabhaengige Plattform zu
migrieren.

Ich habe bei meiner Applikation bewusst auf die Verwendung von
string-switches (selbstgebaut oder mit Java 7) verzichtet, eben
weil ich mich nicht von spezifischen hashcode-Implementierungen
abhaengig machen moechte. Eine Aenderung von hashcode sollte
bei mir daher kein Problem darstellen (oder durch meine Deployment-
Chain recht schnell offensichtlich werden).
Post by Florian Weimer
Post by Lothar Kimmeringer
Das hash-Feld in String dient doch AFIAK nur dem Caching, damit
man nicht staendig aufs neue kalkulieren muss (hash32 kenne ich
jetzt nicht so genau, bin noch primaer in Java 6 unterwegs, duerfte
aber aehnlich gedacht sein).
Ja, das Cache-Feld ist aber bei der gegenwärtigen Implementierung
ziemlich wünschenswert, weil sonst get() oder containsKey() gefolgt
von put() schnell um einen Faktor zwei langsamer wird.
Gegen Caching sagt auch keiner was. Der Algorithmus, der bei der
hash-Berechnung verwendet wurde, spielt fuer's Caching ja auch
keine Rolle.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Florian Weimer
2012-12-30 21:34:52 UTC
Permalink
Post by Lothar Kimmeringer
Post by Florian Weimer
Post by Lothar Kimmeringer
Bei sowas kann man sich ruhig ein bisschen Zeit lassen. Lieber ein
wenig Grundwissen durch Anschauen von Vortraegen sammeln, als mal
schnell was zu implementieren, was am Ende auch nicht besser ist.
Ein Video ist eine ziemlich ungeeignete Weise, solches Wissen zu
vermitteln.
Sondern?
Ein wissenschaftlicher Aufsatz wäre nicht schlecht. Grundsätzlich
krankt das aber daran, daß es so richtig harte Kriterien für gute
Hash-Funktionen nicht gibt, zumindest nicht in der öffentlichen
Literatur.
Post by Lothar Kimmeringer
Post by Florian Weimer
Post by Lothar Kimmeringer
Das wird ja erst ab Java 7 verwendet, unsere Installationsbasis
ist aber groesstenteils noch Java 6.
Holla. Dan sind Hash-Kollisionen aber Dein geringstes Problem.
Die da waeren?
Naja, keine Unterstützungvon TLS-Versionen nach 1.0, keine
Hostnamen-Verifikation bei TLS fallen mir spontan ein. Für mehr müßte
ich jetzt zu graben anfangen.
Post by Lothar Kimmeringer
Post by Florian Weimer
Deswegen ändert sich der Rückgabewert von hashCode() auch nicht.
Warum sollte bei geaendertem hashcode nicht ein anderer Rueckgabe-
wert kommen?
Weil die bisherigen OpenJDK-Updates den Hash-Code gar nicht ändern.
Es gibt einen zweiten Hash-Code, der unabhängig vom offiziellen
Hash-Code ist (und der beliebig geändert werden kann). Deswegen werden
auch alle Hashtable/Map-Implementierungen getrennt gepatcht.
Post by Lothar Kimmeringer
Post by Florian Weimer
Bei Java gibt es keine andere Möglichkeit, weil der Wert von
hashCode() für Strings Teil der Spezifikation ist, siehe z.B. hier
Das war dann aber eine semischlaue Entscheidung.
Ja, das ist in der Tat nicht besonders lustig.
Post by Lothar Kimmeringer
Post by Florian Weimer
Post by Lothar Kimmeringer
Ich persoenlich halte es fuer besser, schlecht programmierte Software
auf die Nase fallen zu lassen. Das sind im Endeffekt weniger betroffene
Systeme als die, die beim Nichtfix auf einfachste Weise fuer DoS-
Angriffe anfaellig sind (nicht unbedingt von _u32 auf _u33, sondern
von Java x auf Java x+1).
Und genau das macht OpenJDK ja auch: wenn der Code aus nicht
vertrauenswürdiger Quelle größere Datenmengen akzeptiert, hat er
sicherlich auch andere Probleme. 8-)
Fakt ist: Der Aufwand bei einer durchschnittlichen Java-Applikation,
an allen Stellen, bei der man externe Daten in eine HashMap packt
(oder bei der auf andere Weise mit hashcode gearbeitet wird), ent-
sprechende Anpassungen einzubauen, duerfte durchaus vergleichbar
sein, das Teil auf eine andere plattformunabhaengige Plattform zu
migrieren.
Das ist sicherlich richtig, aber wenn keine Größenbeschränkungen
vorhanden sind, schickt der potentielle Angreifer einfach ein paar
Dutzend Megabyte, die in der internen Darstellung schnell um ein
Vielfaches aufgeblasen werden und die JVM in einem recht undefinierten
Zustand hinterlassen, wenn der OutOfMemoryError zuschlägt.

Idealerweise müßte man nur den auslösenden Thread abbrechen (auch
dann, wenn er unerwartet viel CPU-Zeit zur Bearbeitung einer Anfrage
benötigt), aber das gibt's da draußen in stabil nur für ganze
Betriebssystem-Prozesse. Ich bin mir nicht mal sicher, ob das Erlang
das hat. Racket (né PLT Scheme) hat "custodians", die zumindest in
bestimmten Builds solche Funktionalität unterstützen.
Sven Köhler
2012-12-31 02:00:14 UTC
Permalink
Post by Lothar Kimmeringer
Post by Florian Weimer
Post by Lothar Kimmeringer
Gibt es eine Moeglichkeit fuer Java 1.6 und groesser, ohne
Anpassung der verwendeten VM (JDK bzw. JRE) java.lang.String
durch eine eigene Klasse zu ersetzen?
Mit Bytecode-Rewriting geht das in jedem Fall.
Genau das wuerde ich gerne vermeiden, ich dachte da eher an
etwas wie eine eigene Klasse im Classpath vor die offizielle legen.
Die Idee solltest du dir aus dem Kopf schlagen, da es seit der
Einführung von Switch-Case über Strings einen Vertrag über den Hash von
Strings zwischen Compiler und Runtime gibt. Siehe mein anderes Posting.

Guck dir einfach mal den Bytecode zu

String s = ...;
switch (s) {
case "abc":
case "def":
...
}

an.


Grüße,
Sven
Wanja Gayk
2012-12-30 13:31:44 UTC
Permalink
Post by Lothar Kimmeringer
habe mir gerade den Vortrag zum Thema Hashflooding reloaded
angesehen und habe wieder einen Hals...[1]
Da in der Behebung des Bugs Oracle anscheinend die gleiche
GFY-Haltung an den Tag zu legen scheint, moechte ich das bis
zum Fix in die eigenen Haende nehmen.
Eine Randomisierung bei String-Hascodes halte ich für grundsätzlich
problematisch für existiernde Software. Wie oft wird mittlerweile wohl
angenommen, dass gleiche Strigns den gleichen Hashcode haben, und wie
oft wird das JVM-übergreifend sein?
So ein Fix kann für haufenweise subtile Fehler sorgen, die mehr Probleme
verursachen können, als gelegentliche DDOS-Attacken. Natürlch ist da
eine Firma wie Oracle sehr vorsichtig.
Ich nehme eher an, dass die Hersteller von Webservern (etc.) ihre
Software eher anpassen werden müssen (z.B. indem sie ihre Map/Set
implementationen eine Sonderbehandlung für Strings einbauen).

Gruß,
-Wanja-
--
"I'm going to put something to you here, and I think you'd better
listen to this: If we race, if we two race, we could end up with
nothing, so it's up to Eddie. If we don't race each other ,
we've got an opportunity to get first and second."
[Damon Hill, Spa '98, being chased by Ralf who is 5s faster than him]
Lothar Kimmeringer
2012-12-30 13:38:07 UTC
Permalink
Post by Wanja Gayk
Post by Lothar Kimmeringer
habe mir gerade den Vortrag zum Thema Hashflooding reloaded
angesehen und habe wieder einen Hals...[1]
Da in der Behebung des Bugs Oracle anscheinend die gleiche
GFY-Haltung an den Tag zu legen scheint, moechte ich das bis
zum Fix in die eigenen Haende nehmen.
Eine Randomisierung bei String-Hascodes halte ich für grundsätzlich
problematisch für existiernde Software. Wie oft wird mittlerweile wohl
angenommen, dass gleiche Strigns den gleichen Hashcode haben, und wie
oft wird das JVM-übergreifend sein?
Die Randomisierung passiert einmal beim Systemstart. D.h. zwei
verschiedene Javaprozesse liefern bei hashcode("text") zwei
verschiedene Hashwerte. Der Hashwert innerhalb des JVM-Prozesses
bleibt aber bei zwei gleichen Strings weiterhin gleich.

Ansonsten haette hashcode() ja auch keinen Sinn mehr und man
koennte gleich Math.random() zurueckgeben.
Post by Wanja Gayk
So ein Fix kann für haufenweise subtile Fehler sorgen, die mehr Probleme
verursachen können, als gelegentliche DDOS-Attacken. Natürlch ist da
eine Firma wie Oracle sehr vorsichtig.
Es ist eine Sache, vorsichtig zu sein. Eine andere ist es aber,
als einzige gar nicht zu reagieren, waehrend alle um sie herum
schnell an Fixes arbeiten.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Florian Weimer
2012-12-30 15:30:15 UTC
Permalink
Post by Lothar Kimmeringer
Es ist eine Sache, vorsichtig zu sein. Eine andere ist es aber,
als einzige gar nicht zu reagieren, waehrend alle um sie herum
schnell an Fixes arbeiten.
Genau diese Sau wurde schon 2003 durch das Dorf getrieben. Damals war
allerseits Aussitzen angesagt, u.a. von den Python-Leuten, die damals
explizit einbezogen wurden. Das mit den schnellen Fixes entspricht
nicht ganz der Wirklichkeit.
Lothar Kimmeringer
2012-12-30 15:50:30 UTC
Permalink
Post by Florian Weimer
Post by Lothar Kimmeringer
Es ist eine Sache, vorsichtig zu sein. Eine andere ist es aber,
als einzige gar nicht zu reagieren, waehrend alle um sie herum
schnell an Fixes arbeiten.
Genau diese Sau wurde schon 2003 durch das Dorf getrieben. Damals war
allerseits Aussitzen angesagt, u.a. von den Python-Leuten, die damals
explizit einbezogen wurden. Das mit den schnellen Fixes entspricht
nicht ganz der Wirklichkeit.
2003 war das auch eher eine theoretische Geschichte (so wie
bei MD5 erst einmal festgestellt wurde, dass Kollisionen
prinzipiell moeglich seien, bis dann Jahre spaeter auf der Basis
ein neues Intermediate Certificate erstellt wurde).

Ich bezog mich hier auf den Verlauf von
http://www.ocert.org/advisories/ocert-2011-003.html
und
http://www.ocert.org/advisories/ocert-2012-001.html


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Florian Weimer
2012-12-30 15:57:21 UTC
Permalink
Post by Lothar Kimmeringer
Post by Florian Weimer
Post by Lothar Kimmeringer
Es ist eine Sache, vorsichtig zu sein. Eine andere ist es aber,
als einzige gar nicht zu reagieren, waehrend alle um sie herum
schnell an Fixes arbeiten.
Genau diese Sau wurde schon 2003 durch das Dorf getrieben. Damals war
allerseits Aussitzen angesagt, u.a. von den Python-Leuten, die damals
explizit einbezogen wurden. Das mit den schnellen Fixes entspricht
nicht ganz der Wirklichkeit.
2003 war das auch eher eine theoretische Geschichte (so wie
Theoretisch?

<http://mail.python.org/pipermail/python-dev/2003-May/035874.html>
<http://mail.python.org/pipermail/python-dev/2003-May/035909.html>

Das war doch ausgesprochen eindeutig.
Lothar Kimmeringer
2012-12-30 18:25:05 UTC
Permalink
Post by Florian Weimer
Post by Lothar Kimmeringer
2003 war das auch eher eine theoretische Geschichte (so wie
Theoretisch?
<http://mail.python.org/pipermail/python-dev/2003-May/035874.html>
<http://mail.python.org/pipermail/python-dev/2003-May/035909.html>
Das war doch ausgesprochen eindeutig.
Neben Python war auch Perl betroffen und die haben das recht
flott gefixed AFAIR. Im Jetzt bin ich aber von Java betroffen,
nicht von den Ansichten der Python-Truppe. Da Python noch dazu
OS ist, ist ein Selbstfixen IMHO einfacher als hier.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Marcel Müller
2012-12-30 21:40:11 UTC
Permalink
Hallo,
Post by Lothar Kimmeringer
Die Randomisierung passiert einmal beim Systemstart. D.h. zwei
verschiedene Javaprozesse liefern bei hashcode("text") zwei
verschiedene Hashwerte. Der Hashwert innerhalb des JVM-Prozesses
bleibt aber bei zwei gleichen Strings weiterhin gleich.
das ist schon klar, aber sobald sich Auswirkungen von getHashCode im
Filesystem wiederfinden, wird es eng. Ob zulässig oder nicht, jemand
könnte die Annahme getroffen haben, dass Hashwerte ohne Wechsel der
Plattform (JVM oder OS) stabil bleiben.
Post by Lothar Kimmeringer
Post by Wanja Gayk
So ein Fix kann für haufenweise subtile Fehler sorgen, die mehr Probleme
verursachen können, als gelegentliche DDOS-Attacken. Natürlch ist da
eine Firma wie Oracle sehr vorsichtig.
Es ist eine Sache, vorsichtig zu sein. Eine andere ist es aber,
als einzige gar nicht zu reagieren, waehrend alle um sie herum
schnell an Fixes arbeiten.
Ich bin jetzt kein kein Oracle Fanboy, aber ein Fix ist nur dann gut,
wenn die Gegenanzeigen nicht überwiegen. Und ein derartiger Eingriff in
die Reproduzierbarkeit stellt ein beträchtliches Risiko dar. Und im
Gegensatz zu den DOS-Attacken weiß man dann überhaupt nicht woran man
ist. Schließlich sind dann auch (Business-)Anwendungen betroffen, die
niemals Ziel einer solchen Attacke werden könnten.

Kurzum, vielleicht bekämpft man ein solches Problem besser über enge
Timeouts beim Request-Processing. Damit bekommt man zumindest eine
Schadensbegrenzung hin. Die Berechnung guter Hashes war schon immer eine
Kunst für sich. Und nicht zuletzt auch immer ein Kompromiss zwischen
Qualität und Geschwindigkeit. Die Anforderungen an einen Web-Server sind
dabei komplett andere als an eine klassische Business-Anwendung, die
hinter den Fronten werkelt.


Marcel
Florian Weimer
2012-12-30 22:24:07 UTC
Permalink
Post by Marcel Müller
Kurzum, vielleicht bekämpft man ein solches Problem besser über enge
Timeouts beim Request-Processing. Damit bekommt man zumindest eine
Schadensbegrenzung hin.
Eher nicht, da Thread.stop() nie zuverlässig funktioniert hat.
Lothar Kimmeringer
2012-12-30 23:31:32 UTC
Permalink
Post by Lothar Kimmeringer
Hallo,
Post by Lothar Kimmeringer
Die Randomisierung passiert einmal beim Systemstart. D.h. zwei
verschiedene Javaprozesse liefern bei hashcode("text") zwei
verschiedene Hashwerte. Der Hashwert innerhalb des JVM-Prozesses
bleibt aber bei zwei gleichen Strings weiterhin gleich.
das ist schon klar, aber sobald sich Auswirkungen von getHashCode im
Filesystem wiederfinden, wird es eng. Ob zulässig oder nicht, jemand
könnte die Annahme getroffen haben, dass Hashwerte ohne Wechsel der
Plattform (JVM oder OS) stabil bleiben.
Dann nimmt er etwas an, das explizit laut API so nicht der Fall sein muss:

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#hashCode()
| The general contract of hashCode is:
|
| Whenever it is invoked on the same object more than once during
| an execution of a Java application, the hashCode method must
| consistently return the same integer, provided no information
| used in equals comparisons on the object is modified. This integer
| need not remain consistent from one execution of an application
| to another execution of the same application.

Gilt im Prinzip auch fuer String. Ich gehe aber auch davon aus,
dass diverse Anwendungen da draussen von einem auf ewig gleich-
bleibenden Hashcode fuer String ausgehen. Ich behaupte mal frech,
dass da von Oracle eigene Sachen dabei sind, deswegen straeuben
sie sich so.
Post by Lothar Kimmeringer
Post by Lothar Kimmeringer
Post by Wanja Gayk
So ein Fix kann für haufenweise subtile Fehler sorgen, die mehr Probleme
verursachen können, als gelegentliche DDOS-Attacken. Natürlch ist da
eine Firma wie Oracle sehr vorsichtig.
Es ist eine Sache, vorsichtig zu sein. Eine andere ist es aber,
als einzige gar nicht zu reagieren, waehrend alle um sie herum
schnell an Fixes arbeiten.
Ich bin jetzt kein kein Oracle Fanboy, aber ein Fix ist nur dann gut,
wenn die Gegenanzeigen nicht überwiegen.
Hat erst mal nichts mit Oracle zu tun, da ja an den entsprechenden
Stellen wohl weiterhin die gleichen Leute (in geringerer Anzahl) an
den entsprechenden Stellen sitzen duerften, die auch damals schon
Entscheidungen trafen, gewissen Dinge nicht zu fixen.

Aber bei genuegend Druck reagieren sie dann schon. Es gibt da den
Bug, bei der ein SelectChannelConnector unter Last zum haengen kommt,
was einem DoS auf dem Port bedeutet. Oracle hat das fuer Java 8 ge-
fixed, Java 7 nicht vorgesehen. Dann gabs einen Shitstorm, mit dem
Effekt, dass es einen Backport auf Java 7 gab. Dann gab es nochmal
einen von Fujitsu Siemens, jetzt arbeiten sie an einem Backport
fuer Java 6 ;-)
Post by Lothar Kimmeringer
Kurzum, vielleicht bekämpft man ein solches Problem besser über enge
Timeouts beim Request-Processing. Damit bekommt man zumindest eine
Schadensbegrenzung hin.
Hat Florian schon geschrieben, warum das nicht so toll ist. Die bis-
herige Vorgehensweise bei den einzelnen Webserveranbietern war die
Einschraenkung der Anzahl von Parametern.
Post by Lothar Kimmeringer
Die Berechnung guter Hashes war schon immer eine
Kunst für sich. Und nicht zuletzt auch immer ein Kompromiss zwischen
Qualität und Geschwindigkeit. Die Anforderungen an einen Web-Server sind
dabei komplett andere als an eine klassische Business-Anwendung, die
hinter den Fronten werkelt.
Momentan stehen die Webserver in vorderster Front, weil die am
einfachsten zu erreichen sind. Wenn die alle ihre Workarounds soweit
eingebaut haben, wird man sich mit den weiteren Tiers beschaeftigen.
Jeder angehende Java-Programmierer muss das lernen, damit nicht
staendig aufs neue die gleichen Probleme aufgerissen werden, etc.
Alles, weil man die eine Methode nicht anpassen will...

Aber alle naselang die JDBC-API erweitern, so dass man staendig
neue Treiber schreiben darf, das ist kein Problem (wobei das mit
der Uebernahme durch Oracle in der Zukunft eventuell besser wird ;-)
Oder in 1.3 Systen.getenv() einen Error schmeissen, es aber ab 1.4
wieder ermoeglichen.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Sven Köhler
2012-12-31 01:40:25 UTC
Permalink
Post by Lothar Kimmeringer
Hallo,
habe mir gerade den Vortrag zum Thema Hashflooding reloaded
angesehen und habe wieder einen Hals...[1]
Mir der Einführung von Switch-Case über Strings ist das Kind glaube
endgültig in den Brunnen gefallen. AFAIK muss der hash eines Strings zur
Compile-Zeit bekannt sein, denn der Compiler generiert aus einem
Switch-Case mit einem String als Argument nämlich ein Switch-Case über
den Hash des Strings. Die Hashfunktion funktioniert kann man also nicht
mehr ändern.

Was ich mich aber schon immer fragt habe, ist warum man die Hashfunktion
nicht beim erzeugen der HashMap austauschen kann - genauso wie man auch
nen Comperator austauschen kann. Es ist IMHO keine Große erkenntnis,
dass für jede Hashfunktion ein Worstcase-Datenmenge existiert, und man
die Hashfunktion manchmal auf die zu erwartende Datenmenge zuschneiden muss.


Grüße,
Sven
m***@heinerkuecker.de
2012-12-31 16:58:01 UTC
Permalink
Post by Lothar Kimmeringer
habe mir gerade den Vortrag zum Thema Hashflooding reloaded
Gibt es eine Moeglichkeit fuer Java 1.6 und groesser, ohne
Anpassung der verwendeten VM (JDK bzw. JRE) java.lang.String
durch eine eigene Klasse zu ersetzen?
Da würde ich die Finger von lassen,
String und HashMap sind in Java so fundamental,
dass es hier eigentlich nur knallen kann.

Die App kann ganz einfach durch eine eigene Map-Implementierung,
welche eine beim Start zufällig erzeugten(und natürlich nicht veröffentlichten) String vorn oder hinten an den Key-String anhängt.

Dann ist der Hashcode nicht mehr durch einen Hacker nachvollziehbar.

Ich verstehe auch nicht, wieso die Java-Web-Server Post-Data cachen sollten
(steht so in dem Foliensatz), für Post-Data gibt es einen Stream, welcher nur im Request-Scope lebt.

Cachen macht eigentlich nur beim Zugriff auf die JSP-Servlet-Klassen
(und deren Name wird erst nach erfolgreichem Lookup als Key in eine Map gesetzt)
sowie bei generierten Antwort-Daten(funktionales Prinzip, gleicher Parameter, gleiche Antwort) Sinn.

Bei letzterem müsste der Angreifer erst mal eine solche cacheable Antwort bekommen und jeder vernünftige Server-Programmierer würde das mit einer LRU-Map oder Soft-Referenzen begrenzen.

Damit das Problem auftritt, muss der Angreifer erst mal überhaupt 2 hoch was weiss ich Requests an den Server bringen, was wahrscheinlich auch schon in ein Sicherheitsnetz laufen würde.

Wo ist da also das Problem.

Grüße
Heiner
Sven Köhler
2012-12-31 17:13:26 UTC
Permalink
Post by m***@heinerkuecker.de
Ich verstehe auch nicht, wieso die Java-Web-Server Post-Data cachen sollten
(steht so in dem Foliensatz), für Post-Data gibt es einen Stream, welcher nur im Request-Scope lebt.
Die POST-Daten werden geparsed, und als key-value Paare in einer HashMap
abgelegt. Diese HashMap ist Teil des HttpServletRequests den ein Servlet
übergeben bekommt.
Lothar Kimmeringer
2013-01-02 12:44:39 UTC
Permalink
Post by m***@heinerkuecker.de
Post by Lothar Kimmeringer
habe mir gerade den Vortrag zum Thema Hashflooding reloaded
Gibt es eine Moeglichkeit fuer Java 1.6 und groesser, ohne
Anpassung der verwendeten VM (JDK bzw. JRE) java.lang.String
durch eine eigene Klasse zu ersetzen?
Da würde ich die Finger von lassen,
String und HashMap sind in Java so fundamental,
dass es hier eigentlich nur knallen kann.
Darauf wird's wohl hinauslaufen (hab mir freigenommen, ich werde
mich damit erst wieder naechste Woche in praktischer Form be-
schaeftigen). Da aber so oder so ein Lerneffekt da sein wird,
werde ich es trotzdem mal versuchen.
Post by m***@heinerkuecker.de
Die App kann ganz einfach durch eine eigene Map-Implementierung,
welche eine beim Start zufällig erzeugten(und natürlich nicht
veröffentlichten) String vorn oder hinten an den Key-String anhängt.
Das Argument kam oefter, ist aber meiner Ansicht nach ein
Herumgepfriemel um das eigentliche Problem. So muss jeder,
der mit HashMap arbeitet, dieses Sicherheitsloch im Kopf
haben, waehrend es mit der Aenderung einer einzigen Methode
fuer alle bestehenden und zukuenftigen Applikationen
gefixed waere.

HTTP Parameter Parsing ist ja nur eine Teilanwendung von Maps
und Hashcode.
Post by m***@heinerkuecker.de
Dann ist der Hashcode nicht mehr durch einen Hacker nachvollziehbar.
Das Ergebnis sollte nicht voraussagbar sein oder am besten
so kollisionsfrei, dass ein Versuch, Kollisionen zu berechnen
zu viel Aufwand bedeutet, um zweckmaessig zu sein. Das Anhaengen
von zufaelligem Text kann funktionieren, muss aber nicht
(siehe MD5).
Post by m***@heinerkuecker.de
Ich verstehe auch nicht, wieso die Java-Web-Server Post-Data cachen sollten
(steht so in dem Foliensatz), für Post-Data gibt es einen Stream, welcher
nur im Request-Scope lebt.
Der Begriff caching duerfte verwirrend eingesetzt sein. Es geht
um das Hinterlegen von Key-Value-Paaren in einer Map, um im
Anschluss schneller darauf zuzugreifen. Im Prinzip ist das
ein Caching, da es ja technisch nicht notwendig ist, sondern nur
die Performance erhoeht.
Post by m***@heinerkuecker.de
Damit das Problem auftritt, muss der Angreifer erst mal überhaupt
2 hoch was weiss ich Requests an den Server bringen, was
wahrscheinlich auch schon in ein Sicherheitsnetz laufen würde.
Wo ist da also das Problem.
Es werden nicht 2 hoch x Anfragen reingestellt, sondern ein Request
mit 2 hoch x Key-Value-Paaren, deren Key immer den gleichen
Hashcode produzieren.

HashMap und Hashtable arbeiten beide nach dem gleichen Prinzip
(bei Hashtable war das frueher anders, aber ich schau jetzt
nicht nach, ob das immer noch so ist), dass sie ein internes
Array mit LinkedList haben. Beim put wird
key.hashcode modulo arraygroesse
errechnet und der LinkedList an der Stelle der Value hinzugefuegt
(ist nicht vollstaendg, siehe spaeter).

Beim get wird dann auf die gleiche Weise die LinkedList ermittelt
und ueber die Elemente iteriert, bis currentkey.equals(key) ist
und der entsprechende Wert zurueckgegeben.

Best case: O(1)
Worst Case: O(n^2)

Der Hack realisiert den Worst Case. Da beim put oben ja schon
geschaut werden muss, ob der Key nicht schon in der Map drin ist,
hast du den Worst Case bereits beim Aufbau der Map, d.h. ob die
angefragte Seite die Parameter ueberhaupt verwendet ist nicht
relevant.

Der simpelste Workaround ist die Nutzung von TreeMap, allerdings
erkauft man sich das mit einem O(log n) im Best Case. Der einfachste
Fix waere die Anpassung von String.hashcode. SUN/Oracle geht aber
wohl (nicht zu unrecht) davon aus, dass es Schlaumeier gibt, die
von einer auf ewig gueltigen Hashimplementierung von String
ausgehen und diese als Konstanten irgendwo im Code verwenden.
Praktisch duerfte das sogar vom Compiler so jetzt selbst bei den
String-cases der Fall sein (siehe
<news:5ng8ph6nu1sw$***@kimmeringer.de>
wie sowas implementiert sein duerfte). Da koennte man aber den
Compiler relativ einfach anpassen, indem man im switch nicht
einfach mystring.hashCode() aufruft, sondern (das derzeit nicht
existente) System.stringHashCode(), aehnlich wie es dort ja schon
den identitiyHash gibt.

Hoffe, das Problem damit verstaendlich gemacht zu haben. Man steht
also derzeit vor der Wahl, alle seine Verwendungen von HashMap
zu aendern und darauf zu hoffen, dass Drittanbieter von Software
wie Jetty, Glassfish etc. ebenfalls schnell etwas liefern (und
dabei nichts uebersehen haben, wie das bei Ruby mit dem Hack von
letztem Jahr passiert ist). Oder man hofft darauf, dass Oracle
einen von einem Big Player eins auf den Deckel bekommt und dazu
"ueberredet" wird, das ganze prinzipiell zu fixen (also in etwa
aequivalent zu den "freiwilligen" Backports beim Bug
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6346658 mit
dem Backport auf Java 6 unter
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8003275)

Ich persoenlich hoffe auf letzteres. Da einem sowieso nur
das Warten bleibt, kann ich mich (theoretisch, mit eventuell
praktischem Einsatz bei Bedarf) mit der Frage beschaeftigen,
ob ich das nicht selbst hintenrum gefixed bekomme. ;-)


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Dietz Proepper
2013-01-02 13:48:19 UTC
Permalink
Post by Lothar Kimmeringer
Post by m***@heinerkuecker.de
Die App kann ganz einfach durch eine eigene Map-Implementierung,
welche eine beim Start zufällig erzeugten(und natürlich nicht
veröffentlichten) String vorn oder hinten an den Key-String anhängt.
Das Argument kam oefter, ist aber meiner Ansicht nach ein
Herumgepfriemel um das eigentliche Problem. So muss jeder,
der mit HashMap arbeitet, dieses Sicherheitsloch im Kopf
haben,
Äh, ja. Das steht in der Doku zu Hashindizierung, es *kann* entarten. Und ja,
das eine oder andere Mal ist mir sowas auch mit nicht explizit dafür
geforge'ten Daten passiert. Nicht in fataler Weise, aber wenn einzelne buckets
10x soviel Elemente haben wie andere, dann ist das ggf. merklich.
Post by Lothar Kimmeringer
waehrend es mit der Aenderung einer einzigen Methode
fuer alle bestehenden und zukuenftigen Applikationen
gefixed waere.
Oder man verwendet eine der 100 anderen Möglichkeiten, das Problem zu
mitigrieren, z.B. Begrenzung der Zahl der Eingabeparameter.

Das Problem auch mit einem "sicheren" hash ist halt, dass "sicher" erstmal
immer schwierig ist, d.h. es richtig zu machen, kann aufwändig werden,
insbesondere, wenn man vermeiden möchte, bestehendes zu zerbrechen. Ja,
Allgemeinplatz.
Sven Köhler
2013-01-02 19:36:37 UTC
Permalink
Post by m***@heinerkuecker.de
Dann ist der Hashcode nicht mehr durch einen Hacker nachvollziehbar.
Das Ergebnis sollte nicht voraussagbar sein ...
Der Rückgabewert von String.hashCode() muss aber vorhersagbar sein, da
sonst Java 7 switch/case mit Strings nicht funktioniert.

Ich hoffe, du willst also - statt String.hashCode() zu verändern - eine
alternative HashMap bauen.



Grüße,
Sven
Dietz Proepper
2013-01-02 19:59:38 UTC
Permalink
Post by Sven Köhler
Post by m***@heinerkuecker.de
Dann ist der Hashcode nicht mehr durch einen Hacker nachvollziehbar.
Das Ergebnis sollte nicht voraussagbar sein ...
Der Rückgabewert von String.hashCode() muss aber vorhersagbar sein, da
sonst Java 7 switch/case mit Strings nicht funktioniert.
Nicht notwendigerweise. Zum Einen könnte man natürlich zusätzlich ein
secureHash() einführen und geeignet verwenden. Zum Anderen könnte die VM die
jeweils gültigen Hash-Werte pro (VM-)Invokation berechnen und verwenden. Der
Overhead wäre vermutlich übersichtlich.
Post by Sven Köhler
Ich hoffe, du willst also - statt String.hashCode() zu verändern - eine
alternative HashMap bauen.
Naja, für den normalen Anwendungsfall ist die gegebene Implementierung
vollständig ausreichend. Man sollte halt wissen, wie sich Hashindizierungen
verhalten können.

Für das Problem der Eingaben aus unbekannten und potentiell bösartigen Quellen
hat mir die Variante, erstmal alles zu cachen ("in wasauchimmer-Map werfen")
und dem Verbraucher komplett zu übergeben noch nie so richtig gefallen.

Ist zwar historisch so üblich, mir wäre aber ein Ansatz, bei dem ich dem
Framework sagen kann "ich erwarte foo, buzz und acme als Parameter, wenn was
anders kommt, dann nerv' mich nicht und geh sofort zur Fehlerseite" mit
geeignetem Parameterparser lieber.
Sven Köhler
2013-01-02 23:38:39 UTC
Permalink
Post by Dietz Proepper
Post by Sven Köhler
Post by m***@heinerkuecker.de
Dann ist der Hashcode nicht mehr durch einen Hacker nachvollziehbar.
Das Ergebnis sollte nicht voraussagbar sein ...
Der Rückgabewert von String.hashCode() muss aber vorhersagbar sein, da
sonst Java 7 switch/case mit Strings nicht funktioniert.
Nicht notwendigerweise. Zum Einen könnte man natürlich zusätzlich ein
secureHash() einführen und geeignet verwenden.
Und wem soll die nützen, wenn man nicht zugleich die HashMap von
hashCode() auf secureHash() umschreibt?
Post by Dietz Proepper
Zum Anderen könnte die VM die
jeweils gültigen Hash-Werte pro (VM-)Invokation berechnen und verwenden. Der
Overhead wäre vermutlich übersichtlich.
Verstehe ich nicht. Da stehen HashWerte von Strings im ByteCode, und
werden mit dem Rückgabewert von hashCode() verglichen. hashCode() muss
das tun, was es immer tut. Anonsten funktioniert der vom Compiler
erzeugte Bytecode nicht wie gewünscht.


Grüße,
Sven
Dietz Proepper
2013-01-03 08:58:16 UTC
Permalink
Post by Sven Köhler
Post by Dietz Proepper
Post by Sven Köhler
Der Rückgabewert von String.hashCode() muss aber vorhersagbar sein, da
sonst Java 7 switch/case mit Strings nicht funktioniert.
Nicht notwendigerweise. Zum Einen könnte man natürlich zusätzlich ein
secureHash() einführen und geeignet verwenden.
Und wem soll die nützen, wenn man nicht zugleich die HashMap von
hashCode() auf secureHash() umschreibt?
Niemandem. Daher stand oben auch "geeignete Verwendung".
Post by Sven Köhler
Post by Dietz Proepper
Zum Anderen könnte die VM die
jeweils gültigen Hash-Werte pro (VM-)Invokation berechnen und verwenden.
Der Overhead wäre vermutlich übersichtlich.
Verstehe ich nicht. Da stehen HashWerte von Strings im ByteCode, und
werden mit dem Rückgabewert von hashCode() verglichen.
Der Classloader könnte die Hashwerte zur Laufzeit im Bytecode entsprechend
anpassen, z.B.. Macht z.B. der elf-Loader für Sprungzieladressen auch.
Post by Sven Köhler
hashCode() muss das tun, was es immer tut.
Nein.
Post by Sven Köhler
Anonsten funktioniert der vom Compiler erzeugte Bytecode nicht wie
gewünscht.
Es gäbe viable Umwege.
Sven Köhler
2013-01-03 18:15:37 UTC
Permalink
Post by Dietz Proepper
Post by Sven Köhler
Verstehe ich nicht. Da stehen HashWerte von Strings im ByteCode, und
werden mit dem Rückgabewert von hashCode() verglichen.
Der Classloader könnte die Hashwerte zur Laufzeit im Bytecode entsprechend
anpassen, z.B.. Macht z.B. der elf-Loader für Sprungzieladressen auch.
Ich würde wetten, dass der Hash von Strings in der Language
Specification oder an einer anderen Stelle definiert wurde, damit der
Compiler annahmen darüber machen darf.

Das Ende vom Lied ist, dass jeder Annahmen über die Rückgabe von
hashCode machen darf. Ob ein Integer Wert im Bytecode nun mit einem Hash
von einem String verglichen wird, kann man nur bedingt rausfinden.
Desweiteren müsste man den String kennen, um dessen Hashwert
neuzuberechnen. Auch das ist nur bedingt möglich.

Und selbst, wenn man nur switch/case über Strings finden müsste,
so müsste man den Bytecode beim Laden via pattern matching auf alles
untersuchen, was mal ein switch/case über Strings gewesen sein könnte.
Weisst du, wie viele mögliche Varianten von Bytecode es geben könnte für
ein switch/case über Strings? Um dich zu ärgern lasse ich natürlich
einfach mal einen ByteCode Optimizer drüberlaufen.

Kann es sein, dass du dir das alles ziemlich einfach vorstellst?

Ein ELF-binary enthält AFAIK Metainformtionen darüber, an welchen
Stellen Sprungadressen angepasst werden müssen. Das von dir
Vorgeschlagene aufspüren von ehemaligen switch/case Anweisungen im
Bytecode dürfte ungleich komplexer sein.



Grüße,
Sven
Lothar Kimmeringer
2013-01-03 11:47:01 UTC
Permalink
Post by Sven Köhler
Verstehe ich nicht. Da stehen HashWerte von Strings im ByteCode, und
werden mit dem Rückgabewert von hashCode() verglichen. hashCode() muss
das tun, was es immer tut. Anonsten funktioniert der vom Compiler
erzeugte Bytecode nicht wie gewünscht.
Ich zitier mich einfach mal selbst, wie das aussehen koennte:

| Da koennte man aber den
| Compiler relativ einfach anpassen, indem man im switch nicht
| einfach mystring.hashCode() aufruft, sondern (das derzeit nicht
| existente) System.stringHashCode(), aehnlich wie es dort ja schon
| den identitiyHash gibt.

Beachte, dass es um den Teil im switch geht, nicht um die fixen
Werte bei den einzelnen Case-Teilen.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Sven Köhler
2013-01-03 18:44:12 UTC
Permalink
Post by Lothar Kimmeringer
Post by Sven Köhler
Verstehe ich nicht. Da stehen HashWerte von Strings im ByteCode, und
werden mit dem Rückgabewert von hashCode() verglichen. hashCode() muss
das tun, was es immer tut. Anonsten funktioniert der vom Compiler
erzeugte Bytecode nicht wie gewünscht.
| Da koennte man aber den
| Compiler relativ einfach anpassen, indem man im switch nicht
| einfach mystring.hashCode() aufruft, sondern (das derzeit nicht
| existente) System.stringHashCode(), aehnlich wie es dort ja schon
| den identitiyHash gibt.
Beachte, dass es um den Teil im switch geht, nicht um die fixen
Werte bei den einzelnen Case-Teilen.
Du kannst jede Menge Dinge tun, so dass der erzeugte Bytecode auf
älteren JDKs nicht mehr läuft. Das ist mir alles klar.

Oracle müsste sich aber, um die Lücke so zu stopfen dass möglichst alle
Anwendungen davon profitieren, auf andere Mittel beschränken. Möglichst
ohne, dass diese Anwendungen neu übersetzt werden müssen.

Ich kann mich irren, aber war das nicht deine ursprüngliche Intention,
als du vorschlugst String.hashCode abzuändern?


Auf die Gefahr hin, dass ich mich wiederhole:
Der Rückgabewert von String.hashCode() ist vorgeschrieben. Du kannst die
Funktion meinetwegen ändern wie du lustig bist. Aber abwärtskompatibel
bist du damit nicht.


Grüße,
Sven
Lothar Kimmeringer
2013-01-04 15:54:30 UTC
Permalink
Post by Sven Köhler
Oracle müsste sich aber, um die Lücke so zu stopfen dass möglichst alle
Anwendungen davon profitieren, auf andere Mittel beschränken. Möglichst
ohne, dass diese Anwendungen neu übersetzt werden müssen.
Ich kann mich irren, aber war das nicht deine ursprüngliche Intention,
als du vorschlugst String.hashCode abzuändern?
Mein "Vorschlag" war erst mal konkret auf die Anwendung von mir
selbst bezogen, von der ich weiss, dass sie nirgendwo spezifische
Annahmen ueber die Implementierung von String.hashCode macht.

Bei einem Irrtum meinerseits wuerde das aber einer meiner Testcases
aufzeigen, so dass ich da recht entspannt waere.
Post by Sven Köhler
Der Rückgabewert von String.hashCode() ist vorgeschrieben.
Die Implementierung steht zwar im Javadoc beschrieben, jedoch
steht da nicht, dass diese auf ewig so sein wuerde. Ansonsten
gilt wohl die Beschreibung von Object.hashCode und da steht
explizit drin, dass keine Annahmen ueber die Konstanz von
hashCode-Ergebnisse ueber verschiedene Prozesse hinweg gemacht
werden duerfen.
Post by Sven Köhler
Du kannst die
Funktion meinetwegen ändern wie du lustig bist. Aber abwärtskompatibel
bist du damit nicht.
Hat auch keiner verlangt. Waer auch nicht das erste mal, dass
das passiert. Eine Abwaertskompatibilitaet waere uebrigens nicht
per se ausgeschlossen: Die VM koennte ja meinetwegen anhand der
Class-Version entscheiden, welcher hashcode-Algorithmus verwendet
wird, um das Problem bei String-switches zu vermeiden (Spontan-
einfall nach einer Sekunde Nachdenken, koennte also im Detail
noch Effekte haben ;-)


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Sven Köhler
2013-01-04 17:32:49 UTC
Permalink
Post by Lothar Kimmeringer
Post by Sven Köhler
Oracle müsste sich aber, um die Lücke so zu stopfen dass möglichst alle
Anwendungen davon profitieren, auf andere Mittel beschränken. Möglichst
ohne, dass diese Anwendungen neu übersetzt werden müssen.
Ich kann mich irren, aber war das nicht deine ursprüngliche Intention,
als du vorschlugst String.hashCode abzuändern?
Mein "Vorschlag" war erst mal konkret auf die Anwendung von mir
selbst bezogen, von der ich weiss, dass sie nirgendwo spezifische
Annahmen ueber die Implementierung von String.hashCode macht.
Und du verwendest auch keine Klasse, die ihrerseits solche Annahme
machen? Ich denke da so an Klassen von der Java Runtime, über die du
keine Kontrolle hast - die du aber zwangsläufig benutzt.
Post by Lothar Kimmeringer
Bei einem Irrtum meinerseits wuerde das aber einer meiner Testcases
aufzeigen, so dass ich da recht entspannt waere.
Die Hoffnung stirbt zuletzt.
Post by Lothar Kimmeringer
Post by Sven Köhler
Der Rückgabewert von String.hashCode() ist vorgeschrieben.
Die Implementierung steht zwar im Javadoc beschrieben, jedoch
steht da nicht, dass diese auf ewig so sein wuerde.
Soll das ein Scherz sein? Darf man jetzt alles ändern, obwohl es
dokumentiert ist? Oder nur Hashfunktionen?

Wann hast du das letzte mal einen JavaDoc Kommentar gelesen welcher
explizit aussagt, dass sich das dokumentierte Verhalten nicht mehr
ändern darf? Schreibst du es selbst immer explizit dazu?
Post by Lothar Kimmeringer
Ansonsten
gilt wohl die Beschreibung von Object.hashCode und da steht
explizit drin, dass keine Annahmen ueber die Konstanz von
hashCode-Ergebnisse ueber verschiedene Prozesse hinweg gemacht
werden duerfen.
Das kann ich beim besten Willen nicht von der JavaDoc von
Object.hashCode aus dem JDK 7 ableiten.

http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode%28%29

Kannst du die relevante Stelle mal zitieren?
Post by Lothar Kimmeringer
Post by Sven Köhler
Du kannst die
Funktion meinetwegen ändern wie du lustig bist. Aber abwärtskompatibel
bist du damit nicht.
Hat auch keiner verlangt. Waer auch nicht das erste mal, dass
das passiert. Eine Abwaertskompatibilitaet waere uebrigens nicht
per se ausgeschlossen: Die VM koennte ja meinetwegen anhand der
Class-Version entscheiden, welcher hashcode-Algorithmus verwendet
wird, um das Problem bei String-switches zu vermeiden (Spontan-
einfall nach einer Sekunde Nachdenken, koennte also im Detail
noch Effekte haben ;-)
Das berücksichtigt das Laden von "altem" ByteCode, allerdings nicht das
kompilieren von altem Sourcecode. Und natürlich nützt es existierenden
Anwendungen ebenfalls nur sehr wenig. Denn diese müssten zumindest neu
Übersetzt werden um von dem Fix zu profitieren. Und natürlich würden sie
danach nicht mehr mit älteren JDKs laufen, was die Mindestanforderungen
erhöht.

Aber nun gut, Abwärtskompatibilität ist nicht gewünscht. Dann sieht die
Welt schon anders aus. Und außerdem spricht in deinem Eröffnungsposting
von 1.6, welches noch gar kein switch/case über Strings unterstützt.

BTW: Was war deiner Meinung nach die größte/auffälligste Änderung bei
der die Abwärtskompatibilität nicht gewährleistet war?


Grüße,
Sven
Lothar Kimmeringer
2013-01-04 18:15:21 UTC
Permalink
Post by Sven Köhler
Post by Lothar Kimmeringer
Die Implementierung steht zwar im Javadoc beschrieben, jedoch
steht da nicht, dass diese auf ewig so sein wuerde.
Soll das ein Scherz sein? Darf man jetzt alles ändern, obwohl es
dokumentiert ist? Oder nur Hashfunktionen?
Im Javadoc steht nur drin, wie die aktuelle Implementierung aussieht,
nicht, dass sie auf ewig so sein muss.
Post by Sven Köhler
Wann hast du das letzte mal einen JavaDoc Kommentar gelesen welcher
explizit aussagt, dass sich das dokumentierte Verhalten nicht mehr
ändern darf? Schreibst du es selbst immer explizit dazu?
Solange nicht explizit dabei steht, dass eine bestimmte Implementierung
auf ewig so sein wird, gehe ich davon aus, dass sie sich auch aendern
kann. Selbst, wenn das so dasteht, gehe ich davon aus, dass sich das
nicht bewahrheiten wird, wenn ein Fehler oder etwas anderes darin
gefunden wird, was eine Aenderung notwendig macht.

Und, ja: Wuerde ich eine Methode schreiben, deren Implementierung
auf ewig so bleiben muss, wuerde ich es explizit hinschreiben, damit
ich in zehn Jahren noch daran denke, dass es so ist.
Post by Sven Köhler
Post by Lothar Kimmeringer
Ansonsten
gilt wohl die Beschreibung von Object.hashCode und da steht
explizit drin, dass keine Annahmen ueber die Konstanz von
hashCode-Ergebnisse ueber verschiedene Prozesse hinweg gemacht
werden duerfen.
Das kann ich beim besten Willen nicht von der JavaDoc von
Object.hashCode aus dem JDK 7 ableiten.
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode%28%29
Kannst du die relevante Stelle mal zitieren?
Das tat ich bereits, es waere schoen, wenn du dir den Thread mal
Post by Sven Köhler
Post by Lothar Kimmeringer
Post by Sven Köhler
Du kannst die
Funktion meinetwegen ändern wie du lustig bist. Aber abwärtskompatibel
bist du damit nicht.
Hat auch keiner verlangt. Waer auch nicht das erste mal, dass
das passiert. Eine Abwaertskompatibilitaet waere uebrigens nicht
per se ausgeschlossen: Die VM koennte ja meinetwegen anhand der
Class-Version entscheiden, welcher hashcode-Algorithmus verwendet
wird, um das Problem bei String-switches zu vermeiden (Spontan-
einfall nach einer Sekunde Nachdenken, koennte also im Detail
noch Effekte haben ;-)
Das berücksichtigt das Laden von "altem" ByteCode, allerdings nicht das
kompilieren von altem Sourcecode.
Natuerlich beruecksichtigt es das, weil dann eben "System.legacyHashCode"
augerufen wird und nicht mehr String.hashCode (habe ich schon geschrieben,
daher spar ich mir die Details)
Post by Sven Köhler
Und natürlich nützt es existierenden
Anwendungen ebenfalls nur sehr wenig. Denn diese müssten zumindest neu
Übersetzt werden um von dem Fix zu profitieren. Und natürlich würden sie
danach nicht mehr mit älteren JDKs laufen, was die Mindestanforderungen
erhöht.
Oder man stellt mehrere Jars fuer die verschiedenen Java-Versionen
zur Verfuegung, wie das schon bei JDBC-Treibern oder anderen Libraries,
wie zum Beispiel BouncyCastle notwendig ist.
Post by Sven Köhler
BTW: Was war deiner Meinung nach die größte/auffälligste Änderung bei
der die Abwärtskompatibilität nicht gewährleistet war?
Das oben genannte System.getenv(), das bei Java 1.4
ploetzlich einen Error schmiss und damit bestehende Applikationen,
die das aus welchen Gruenden auch immer brauchten, in den Abgrund
rissen. Mit der naechsten Java-Version wurde das Verhalten uebrigens
wieder rueckgaengig gemacht.

Siehe
http://jcapi.cvs.sourceforge.net/viewvc/jcapi/net/sourceforge/jcapi/rcapi/RCapi.java?view=markup
wo es einen Grund fuer getenv gibt und wie das Geheinze aussieht,
das man dafuer aufwenden musste, damit die Applikation weiterhin auf
allen JVMs laeuft.

Ob's die augenfaelligste Aenderung war, weiss ich nicht. Es war
auf jeden Fall die Aenderung, die mich in der fraglichen Zeit
am meissten "gefreut" hat.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Sven Köhler
2013-01-04 19:15:17 UTC
Permalink
Post by Lothar Kimmeringer
Post by Sven Köhler
Post by Lothar Kimmeringer
Die Implementierung steht zwar im Javadoc beschrieben, jedoch
steht da nicht, dass diese auf ewig so sein wuerde.
Soll das ein Scherz sein? Darf man jetzt alles ändern, obwohl es
dokumentiert ist? Oder nur Hashfunktionen?
Im Javadoc steht nur drin, wie die aktuelle Implementierung aussieht,
nicht, dass sie auf ewig so sein muss.
Wie bitte!?

Man beschreibt auch nicht die Implementierung, sondern man beschreibt
den "Vetrag", den man mit demjenigen eingeht, der die Method benutzt.

Beinhaltet der Vertrag, dass die Methode konstante Laufzeit hat, dann
schreibst du das ins JavaDoc. Wie die implementierung aussieht, ist
dabei egal, so lange sie den Vertrag erfüllt.

Dies ist bei der kompletten JavaDoc des JDK so.

Und wenn der Vertrag vorsieht, dass ein bestimmtes Polynom ausgewertet
wird (wie bei String.hashCode()) das hat sich die Implementierung daran
zu halten.
Post by Lothar Kimmeringer
Post by Sven Köhler
Wann hast du das letzte mal einen JavaDoc Kommentar gelesen welcher
explizit aussagt, dass sich das dokumentierte Verhalten nicht mehr
ändern darf? Schreibst du es selbst immer explizit dazu?
Solange nicht explizit dabei steht, dass eine bestimmte Implementierung
auf ewig so sein wird, gehe ich davon aus, dass sie sich auch aendern
kann. Selbst, wenn das so dasteht, gehe ich davon aus, dass sich das
nicht bewahrheiten wird, wenn ein Fehler oder etwas anderes darin
gefunden wird, was eine Aenderung notwendig macht.
Es ist ein Unterschied, ob man einen Fehler in einer Implementierung
findet, oder im Vertrag. Ein Fehler kann man überhaupt nur feststellen,
wenn es einen API Vertrag gibt, denn dieser legt fest, was die
Implementierung leisten soll.

String.indexOf() benutze ich nicht, weil mir die Implementierung
gefällt, sondern weil mir zugesichert wird, dass es das erste Auftreten
eines characters zurückgibt. Sollte man jemals festellen, dass es die -2
statt -1 zurückgibt, wenn der character nicht gefunden wird, so wäre
dies ein Fehler, und die Implementierung würde gerändert.
Der API Vertrag bleibt allerdings wie er ist.
Post by Lothar Kimmeringer
Und, ja: Wuerde ich eine Methode schreiben, deren Implementierung
auf ewig so bleiben muss, wuerde ich es explizit hinschreiben, damit
ich in zehn Jahren noch daran denke, dass es so ist.
Du solltest deinen Stil zu Kommentieren dringend ändern, da du
offensichtlich Implementierungen dokumentierst.

Ich weiss, das es gang und gäbe ist, erst alles zu Implementierung, und
sich dann den dazu passenden Vertrag zu überlegen, daber das ist eine
andere Geschichte.
Post by Lothar Kimmeringer
Post by Sven Köhler
BTW: Was war deiner Meinung nach die größte/auffälligste Änderung bei
der die Abwärtskompatibilität nicht gewährleistet war?
Das oben genannte System.getenv(), das bei Java 1.4
ploetzlich einen Error schmiss und damit bestehende Applikationen,
die das aus welchen Gruenden auch immer brauchten, in den Abgrund
rissen. Mit der naechsten Java-Version wurde das Verhalten uebrigens
wieder rueckgaengig gemacht.
Und womit wurde das begründet dass das rückgängig gemacht wurde? Doch
nicht etwa damit, dass die vorhergehende Änderung die
Abwärtskompatibilität oder gar den Vertrag verletzt?


Grüße,
Sven
Lothar Kimmeringer
2013-01-04 19:55:24 UTC
Permalink
Post by Sven Köhler
Post by Lothar Kimmeringer
Post by Sven Köhler
Post by Lothar Kimmeringer
Die Implementierung steht zwar im Javadoc beschrieben, jedoch
steht da nicht, dass diese auf ewig so sein wuerde.
Soll das ein Scherz sein? Darf man jetzt alles ändern, obwohl es
dokumentiert ist? Oder nur Hashfunktionen?
Im Javadoc steht nur drin, wie die aktuelle Implementierung aussieht,
nicht, dass sie auf ewig so sein muss.
Wie bitte!?
Man beschreibt auch nicht die Implementierung, sondern man beschreibt
den "Vetrag", den man mit demjenigen eingeht, der die Method benutzt.
Schau dir das Javadoc an, dort steht die Implementierung beschrieben,
wie ein hashcode von String berechnet wird. Da steht nicht drin, dass
das auf ewig so bleiben wird.
Post by Sven Köhler
Und wenn der Vertrag vorsieht, dass ein bestimmtes Polynom ausgewertet
wird (wie bei String.hashCode()) das hat sich die Implementierung daran
zu halten.
Gut, Implementierung und Vertrag als Begriff nicht sauber verwendet.
Es bleibt aber dabei. In der Javadoc steht nicht drin, dass das
Polynom auf ewig so bleiben muss, d.h. in Java 8 oder 9 koennte
da durchaus ein anderes Polynom oder ein voellig anderer Algorithmus
beschrieben sein.
Post by Sven Köhler
Post by Lothar Kimmeringer
Und, ja: Wuerde ich eine Methode schreiben, deren Implementierung
auf ewig so bleiben muss, wuerde ich es explizit hinschreiben, damit
ich in zehn Jahren noch daran denke, dass es so ist.
Du solltest deinen Stil zu Kommentieren dringend ändern, da du
offensichtlich Implementierungen dokumentierst.
Ich weiss, das es gang und gäbe ist, erst alles zu Implementierung, und
sich dann den dazu passenden Vertrag zu überlegen, daber das ist eine
andere Geschichte.
Meine Art Javadocs zu erstellen kannst du online sehen (den Link
zu einem Beispiel kannst du dir aber jetzt selbst suchen, ich bin
des Newslinkbauens muede ;-) Vielleicht kannst du dann konstruktiver
kritisieren, als du das bisher tust.
Post by Sven Köhler
Post by Lothar Kimmeringer
Post by Sven Köhler
BTW: Was war deiner Meinung nach die größte/auffälligste Änderung bei
der die Abwärtskompatibilität nicht gewährleistet war?
Das oben genannte System.getenv(), das bei Java 1.4
ploetzlich einen Error schmiss und damit bestehende Applikationen,
die das aus welchen Gruenden auch immer brauchten, in den Abgrund
rissen. Mit der naechsten Java-Version wurde das Verhalten uebrigens
wieder rueckgaengig gemacht.
Und womit wurde das begründet dass das rückgängig gemacht wurde? Doch
nicht etwa damit, dass die vorhergehende Änderung die
Abwärtskompatibilität oder gar den Vertrag verletzt?
Im Javadoc fuer 1.4 stand nichts drin, dass ein Error geworfen wird.
Das blieb dem interessierten (d.h. nutzenden) Benutzer auf die Harte
Tour herauszufinden. In Java 5 hat man dann das Javadoc komplett
umgeschrieben und sogar eine neue getenv-Methode hinzugefuegt.
Gruende wurden keine genannt (und ich bin zu faul, jetzt in der
Bugparade nach den eventuell vorhandenen Tickets zu schauen, wo das
eventuell erklaert wird).

Auch hier spare ich mir das erneute Posten von Links. Sollte dem
interessierten Javaentwickler auch so moeglich sein, das zu finden.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Sven Köhler
2013-01-04 20:42:11 UTC
Permalink
Post by Lothar Kimmeringer
Post by Sven Köhler
Man beschreibt auch nicht die Implementierung, sondern man beschreibt
den "Vetrag", den man mit demjenigen eingeht, der die Method benutzt.
Schau dir das Javadoc an, dort steht die Implementierung beschrieben,
wie ein hashcode von String berechnet wird. Da steht nicht drin, dass
das auf ewig so bleiben wird.
Und es steht nicht drin, weil es NIRGENS dabei steht, dass es sich der
Vertrag nicht ändern wird. Das der Vertrag auch in zukünftigen Versionen
eingehalten wird, ist das, was man Abwärtskompatibilität nennt. Und
darauf wird seitens Sun und Oracle geachtet.

Du möchtest, dass es das Polynom nicht Teil des Vertrags ist. Das es
nicht Teil des Vertrags ist, oder das sich das Polynom darf steht da mit
keiner Silbe! Deine Interpretation ist willkürlich.

Auch führst du völlig zu unrecht den Vertrag von Object.hashCode() an.
Die einzige Anforderung an den Vertrags von String.hashCode() muss sein,
dass es den Vertrag von Object.hashCode() nicht verletzt.

Die von die zitierte Stelle aus der Doku von Object.hashCode() sagt aus,
dass der Rückgabewert von hashCode in einer Ausführung für ein Objekt
gleich bleiben muss. Das erfüllt das Polynom auf jeden fall.

Den Vertrag von String.hashCode() mit dem von Object.hashCode()
relativieren zu wollen, ist völlig unzulässig. Wenn ich weiss, dass es
sich um ein Objekt vom Typ X handelt (oder einer Unterklasse von X),
dann ist der Vertrag von X.hashCode() anwendbar.


Grüße,
Sven
Lothar Kimmeringer
2013-01-04 22:09:55 UTC
Permalink
Post by Sven Köhler
Post by Lothar Kimmeringer
Post by Sven Köhler
Man beschreibt auch nicht die Implementierung, sondern man beschreibt
den "Vetrag", den man mit demjenigen eingeht, der die Method benutzt.
Schau dir das Javadoc an, dort steht die Implementierung beschrieben,
wie ein hashcode von String berechnet wird. Da steht nicht drin, dass
das auf ewig so bleiben wird.
Und es steht nicht drin, weil es NIRGENS dabei steht, dass es sich der
Vertrag nicht ändern wird. Das der Vertrag auch in zukünftigen Versionen
eingehalten wird, ist das, was man Abwärtskompatibilität nennt. Und
darauf wird seitens Sun und Oracle geachtet.
Dass das in der Realitaet von SUN schon anders gehandhabt wurde,
habe ich bereits mittels System.getenv gezeigt.

Nur fuer dich habe ich aber mal nach einem anderen Beispiel gesucht
und dabei folgendes gefunden:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4045622

Money Quote:

| To bring the implementation into accord with the specification, and
| to fix the performance problems, we are modifying the specification and
| implementation in tandem. The new String hash function as of JDK1.2 is
| specified to be:
|
| s[0] * 31^(n-1) + s[1] * 31^(n-2) + ... + s[n-1]

So viel dazu...


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Sven Köhler
2013-01-05 01:50:35 UTC
Permalink
Post by Lothar Kimmeringer
Nur fuer dich habe ich aber mal nach einem anderen Beispiel gesucht
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4045622
| To bring the implementation into accord with the specification, and
| to fix the performance problems, we are modifying the specification and
| implementation in tandem. The new String hash function as of JDK1.2 is
|
| s[0] * 31^(n-1) + s[1] * 31^(n-2) + ... + s[n-1]
So viel dazu...
Das war doch jetzt aber völlig in Ordnung, oder? Denn das Polynom war ja
sowieso nicht explizit als "wird sich niemals ändern" gekennzeichnet, oder?


Grüße,
Sven
Lothar Kimmeringer
2013-01-05 11:24:26 UTC
Permalink
Post by Sven Köhler
Post by Lothar Kimmeringer
Nur fuer dich habe ich aber mal nach einem anderen Beispiel gesucht
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4045622
| To bring the implementation into accord with the specification, and
| to fix the performance problems, we are modifying the specification and
| implementation in tandem. The new String hash function as of JDK1.2 is
|
| s[0] * 31^(n-1) + s[1] * 31^(n-2) + ... + s[n-1]
So viel dazu...
Das war doch jetzt aber völlig in Ordnung, oder? Denn das Polynom war ja
sowieso nicht explizit als "wird sich niemals ändern" gekennzeichnet, oder?
Meiner Ansicht widerspricht es nicht. Nur scheint sich deine
Aussage "ein Vertrag ist ein Vertrag ist ein Vertrag" eher
in Luft aufgeloest zu haben, oder?

Alternativ koennte man wohl sagen, dass die Vertraege in Javadocs
nicht das Papier wert sind, auf denen sie nicht gedruckt sind ;-)


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Sven Köhler
2013-01-05 22:50:59 UTC
Permalink
Post by Lothar Kimmeringer
Meiner Ansicht widerspricht es nicht. Nur scheint sich deine
Aussage "ein Vertrag ist ein Vertrag ist ein Vertrag" eher
in Luft aufgeloest zu haben, oder?
Das ist natürlich Quatsch. Denn ohne diese Ansicht könnte man wohl nicht
programmieren, ohne nachts Alpträume zu kriegen.
Das read() bei EOF -1 zurückgibt, ist schließlich auch Teil eines
Vertrages, und Sun/Oracle könnte den Vertrag ändern, und entscheiden:
read() gibt ab jetzt len+1 zurück. Schließlich haben sie ja nicht dabei
geschrieben, dass sich das niemals ändert!


Grüße,
Sven

Stefan Ram
2013-01-05 07:13:13 UTC
Permalink
Subject: Eigenes java.lang.String verwenden
Ich finde, das ist eine contradictio in adiecto!
/Entweder/ man verwendete etwas Eigenes,
/oder/ man verwendet java.lang.String.

Aber, wenn man java.lang.String »modifiziert«, dann befindet
man sich nicht mehr auf der Plattform »Java SE«. Es gibt ja
wohl Open-Source Java-Implementationen, also steht es Dir ja
frei, Deinen eigenen Java-SE-Fork mit einer »sicherere«
String-Implementation anzubieten, gut, vielleicht darfst Du
ihn dann nicht »Java« nennen.

Das Zeitverhalten O(n) für den schlimmsten Fall bei HashMap
ist Programmierern ja grundsätzlich bekannt. Wenn dies nicht
akzeptabel ist, steht es ihnen ja frei, nach etwas anderem
Ausschau zu halten. Na gut, es sind natürlich niemandem, der
mit einer HashMap arbeitet, immer gleich alle denkbaren
Angriffszenarien bewußt, das stimmt schon. Aber, wenn man
jetzt einen Austausch vornimmt, um den bekannten Angriff A
abzuwehren, kann das durchaus einen anderen Angriff B erst
ermöglichen, an den wir jetzt vielleicht gerade nicht denken.

Gegen die von Dir geschilderten Angriffe hilft natürlich
erst einmal das Salzen der eingelesenen Strings.
Loading...