Skip to content

Asynchrones Laden in Guava Cache

Google Guava Cache ist eine weitverbreitete Komponente zur Implementierung von caches. Sie bietet ein Framework um schnell in-memory caches zu implementieren. Unter anderem erlaubt ein CacheLoader den asynchronen refresh von Einträgen. Dabei muss man aber beachten, dass dies nicht out-of-the-box geschieht, sondern dass man dazu eine eigene Logik implementieren muss die das refreshen/laden im Hintergrund implementiert.

Die Grundlagen sind im Guava Wiki beschreiben, ich hab aber aktuell ein Issue offen in dem ich die Dokumentation einer bestehenden Hilfsmethode vorschlage:

Ein kleiner Wrapper wird von Google seit Guava 17 bereitgestellt, man kann damit einen synchronen CacheLoader der keine besondere async-logik enthält wrappen, so dass die refreshes an einen Executor delegiert werden: CacheLoader.asyncRefresh().

Damit lässt sich schnell aus einem vorhandenen CacheLoader ein asynchroner machen. Dabei ist zu beachten dass der Wrapper zusätzliche Objekte erstellt bei jedem re-fresh. Wenn es also auf leichtgewichtige Implementierung ankommt, und wenn eventuell ein Teil des Refresh codes synchron laufen kann, so ist es besser die Logik selbst zu implementieren (man muss nur darauf achten die Erstellung der Future erfolgt synchron, alles was bis zur completion ausgeführt wird läuft im Hintergrund und verzögert keine Lookups).

Folgende Eigenheiten hat das Refreshing:

  • Wenn das Refreshing mittels cache.refresh("key") angestoßen wird, so wartet der refresh Aufruf bis der CacheLoader fertig ist (genauer gesagt, bis die reload future zurückgegeben wurde). Das blockiert nur den Aufrufer-thread, alle anderen Threads die in der Zwischenzeit Cache lookups (für den gleichen Key) machen, bekommen den noch gecachten (alten) Wert.
  • Wenn ein Cache invalidiert wird oder ein Wert aus dem Cache expired, so gibt es keinen alten bestehenden Wert und jeder folgende lookup wird auf das Ende des CacheLoader.load warten müssen. Dabei wird der load pro Key nur einmal gestartet und alle anderen Threads die in dem Zeitraum den Wert anfordern werden angehalten - das ist gut da es die Parallelität des CacheLoader anfragen limitiert. Diese Methode wird nicht in einer asynchronen Variante gewrapped.
  • Wenn refreshing nicht wegen einem direkten refresh(key) Aufruf notwendig wird sondern durch ablaufen des refreshAfterWrite() Zeitraums, so wird das Refreshing durch den nächsten lookup angestoßen. Wenn der CacheLoader synchron ist wird dies auch den (zufällig nächsten) lookup Thread so lange blockieren. Alle weiteren Lookups werden nicht blockiert sondern bekommen den alten Wert. D.h. man muss hier mit regelmäßigen einzelnen verzögerten Lookups rechnen und man kann nicht davon ausgehen dass die Anfragereihenfolge klar in "alte Werte - neue Werte" sortiert werden kann.

The Porting to 11 Theory - The QName Distraction

If you have been using Java for quite some time (since 1.4) and wanted to maintain binary data compatibilitiy, chances are high, that you are relying on the com.sun.xml.namespace.QName.useCompatibleSerialVersionUID=1.0 system property. This will ensure your JVM creates and reads serialized javax.xml.namespace.QName Objects with the same serialisation version UID compatibleSerialVersionUID = 4418622981026545151L. This is especially needed, if you use serialization to store or transmit object trees containing XML objects (and you did not serialize the XML to actual text).

The actual serialVersionUID of QName is meanwhile -9120448754896609940L, but with the compatibility hack you did not only generate objects with the compatible version, but also accepted them.

When you move to Java beyond 8 however, this might break. The backward compatible logic has been removed in JAXP of Java 11. This means your QName instances saved with a not so old Java runtime of 8 (but with the compatibility flag set), suddenly do not work anymore.

Bummer. This is one of the reasons why it is important to follow up on such compatibility hacks and fix them for good. Otherwise it will bite you when you port to newer Java versions just a few decades later.

In JDK11 the compatibility function was removed, with a somewhat sarcastic comment:

  // tests show that the ID is the same from JDK 1.5 through JDK 9
  private static final long serialVersionUID = -9120448754896609940L;

Certainly JDK 1.5 up until 10 did use the default UID, but not if you had used the compatibility flag the whole time.

So the following test code will demonstrates the servialVersionUIDs returned by different Java versions, with and without the compat flag:

Runtime: OpenJDK 64-Bit Server VM 1.8.0_212-b04/25.212-b04 on Windows 10 10.0
Created QName: {}bernd  with UID 4418622981026545151
Serialized: rO0ABXNyABlqYXZheC54bWwubmFtZXNwYWNlLlFOYW1lPVIaMLx2_f8CAA...dAAA

Runtime: OpenJDK 64-Bit Server VM 1.8.0_212-b04/25.212-b04 on Windows 10 10.0
Created QName: {}bernd  with UID -9120448754896609940
Serialized: rO0ABXNyABlqYXZheC54bWwubmFtZXNwYWNlLlFOYW1lgW2oLfw73WwCAA...dAAA

Runtime: OpenJDK 64-Bit Server VM 11.0.1+13-LTS/11.0.1+13-LTS on Windows 10 10.0
Created QName: {}bernd  with UID -9120448754896609940
Serialized: rO0ABXNyABlqYXZheC54bWwubmFtZXNwYWNlLlFOYW1lgW2oLfw73WwCAA...dAAA

Runtime: OpenJDK 64-Bit Server VM 11.0.1+13-LTS/11.0.1+13-LTS on Windows 10 10.0
Created QName: {}bernd  with UID -9120448754896609940
Serialized: rO0ABXNyABlqYXZheC54bWwubmFtZXNwYWNlLlFOYW1lgW2oLfw73WwCAA...dAAA

As you can see, on the JDK11 the compat flag is ignored: only the new default serialVersion UID is used.

With a little modified ObjectInputStream (and there are many reasons to have a custom OIS, like this replacement, but also for hooking into modularized classloaders or doing object filtering) all variantes can be read without the need for a compat flag:

Runtime: OpenJDK 64-Bit Server VM 1.8.0_212-b04/25.212-b04 on Windows 10 10.0

Deserialize Old
 Default OIS read: {}bernd
 My OIS read: {}bernd

Deserialize Default
 Default OIS failed: javax.xml.namespace.QName; local class incompatible: stream classdesc serialVersionUID = -9120448754896609940, local class serialVersionUID = 4418622981026545151
 My OIS read: {}bernd

Runtime: OpenJDK 64-Bit Server VM 11.0.1+13-LTS/11.0.1+13-LTS on Windows 10 10.0

Deserialize Old
 Default OIS failed: javax.xml.namespace.QName; local class incompatible: stream classdesc serialVersionUID = 4418622981026545151, local class serialVersionUID = -9120448754896609940
 My OIS read: {}bernd

Deserialize Default
 Default OIS read: {}bernd
 My OIS read: {}bernd

The code for this tester can be found in a GitHub Gist (including test vectors and complete sample output).

JNDI LDAP with Active Directory with Signing

If you have used JNDI to connect to a Microsoft Active Directory LDAP Server you might see the Warning Event 2886 of source ActiveDirectory_DomainService (every 24 hours) telling you, that you should turn on LDAP Signing. This is also strongly recommended by Microsoft in their latest Security Advisory. They also will turn this on by default in the March 2020 (was January) Windows Server update.

I am not sure if this will actually happen, since a lot of legacy LDAP clients might need to be fixed first. I specifically had a look at Java with the LDAP Naming provider. (Update: as predicted Microsoft has postponed this for now).

If you use a simple bind or a default DIGEST-MD5 with no TLS (LDAPs) and no integrity or confidentiality protection you get the following exception for those Domain Controllers with the additional integrity=2 setting:

javax.naming.AuthenticationNotSupportedException: [LDAP: error code 8 - 00002028: LdapErr: DSID-0C09023C, comment: The server requires binds to turn on integrity checking if SSL\TLS are not already active on the connection, data 0, v4563

AD supports simple Binds, Kerberos (GSSAPI) and DIGEST-MD5 (SASL). The later two option can be configured to work with Request Signing:

  • you must use the fully qualified hostname the LDAP Server think it has in the URL (you might need to make a /etc/hosts entry if your domains are not completely resolved by DNS.
  • you Must enable DIGEST-MD5 as the Security mechanism
  • you must in addition to this request the auth-int quality of protection (otherwise no signing will be recognized)
  • the Account/principal you want to use for logins on the ActiveDirectory domain must have the reversible password encryption turned on and you must set the password after this change. If the user only has a password hash the LDAP directory will not be able to check the password and reject it (unfortunately with the same error as when the password is wrong):
    javax.naming.AuthenticationException: [LDAP: error code 49 - 8009030C: LdapErr: DSID-0C090569, comment: AcceptSecurityContext error, data 52e, v4563

If you use TLS (ldaps) instead, then you don't need to worry about signing. In fact in this case even simple binds will work. (And just for completeness, if you use SASL then you can't request auth-int or auth-conf in the TLS case with Microsoft Directory Service).

Reset Java Web Start (javaws, jp2launcher, JNLP) file associations

Since I was currently playing around with making JavaWeb Start of IcedTea-Web a bit more robust I was collecting the registry keys where JWS would associate itself with files and URL schemes.

As a result I created a Windows Registry export file which DELETES all those locations, so you have a system reset to the beginning (before you reinstall Java SE or OpenJDK installers). Here is the file, in case you need something similar. Note that it only deals with JNLP related entries, no other settings from JDKs are destroyed.

In case you wonder Firefox will remeber the JNLP entries in the handlers.json file in the profile. I make it a habit to delete that file every now and then.

Download: deljnlp.reg

Windows Registry Editor Version 5.00

; deletes most of the file associations dealing 
; with Java Web Start JNLP files and URLs

; !!! use at your own risk !!! regedit /s deljnlp.reg
















[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\MIME\Database\Content Type\application/x-java-jnlp-file]

[-HKEY_CURRENT_USER\Software\Classes\MIME\Database\Content Type\application/x-java-jnlp-file]

[-HKEY_CLASSES_ROOT\MIME\Database\Content Type\application/x-java-jnlp-file]


; TODO the following list might not be complete, if scripting: "*_.jnlp"


[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\javaws.exe]

[-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths\javaws.exe]

[-HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\App Paths\javaws.exe]

[-HKEY_CURRENT_USER\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\App Paths\javaws.exe]





FilterOutputStream - considered harmfull

Java besitzt mit eine Klasse die man als Entwickler extended kann um einen OutputStream zu implementieren der Daten filtert. Dabei implementiert die Basisklasse die unterschiedlichen Varianten von write() sowie flush() und close().

Ich hatte im Rahmen der Vorbereitung für eine Apache Commons VFS 2.1 release geschaut, ob der Code bereits mit Java 8 compiliert werden kann. Es sind noch ein paar Punkte offen, aber was besonders auffiel ist, dass ein Unit-Test fehlschlägt. Nach etwas Analyse (VFS-521) habe ich festgestellt, dass das Problem vom FilterOutputStream in Java 8 herrührt, der ein try-with-resource verwendet um flush() und close() durchzuführen. Wenn dabei beide Methoden die selbe Exception werfen so gibt es beim Hinzufügen der unterdrückten Exception statt der erwarteten IOException eine sehr störende IllegalArgumentException: Self-suppression not permitted.

Wenn man sich den Code älterer Java Implementierungen im Vergleich anschaut, so sieht man allerdings, dass diese noch viel problematischer waren. Denn diese verschlucken bei einem close() potentielle IOExceptions die im davor aufgerufenen flush() aufgetreten sind. Das ältere unsichere Verhalten des FOS ist auch ein "akzeptierter" Bug im JDK: JDK-6335274.

Da der FilteredOutputStream nur verschlimmbessert wurde ist meine Lösung Abstand von dessen close() Methode zu nehmen. Und dies gilt leider auch für den BufferedOutputStream. Hier eine Version ohne besondere Unterstützung für suppressed exceptions.

Mal sehen was auf der openjdk-corelibs-dev liste dazu herauskommt, dort habe ich das Problem mal geschildert. Ein JDK-Bug wurde angelegt, der ist aber noch unbearbeitet.

IPv6 Kongress: Java-Anwendungen für IPv6 fit machen

Ich habe die Gelegenheit auf dem fünften IPv6 Kongress (6.-7. Juni in Frankfurt) über das Thema Java und IPv6 zu sprechen. (Programm). Hier das Abstract des Vortrags:
7.6.2013 14:00 - 14:30 Uhr Bernd Eckenfels: Java-Anwendungen für IPv6 fit machen Dass die Java-Plattform IPv6 unterstützt, ist allgemein bekannt – oder es wird vermutet. Aber sind dazu Änderungen an Anwendungen notwendig, die bisher nur für IPv4 getestet wurden? Welche Funktionen der IPv6-Protokollfamilie werden unterstützt? Welche Besonderheiten sind zu beachten, um Dual-Stack-fähige Anwendungen in Java zu erstellen? Der Vortrag betrachtet die Java-API für Netzwerkkommunikation und untersucht diese auf Relevanz für IPv6. Ein besonderes Augenmerk wird auf die Umsetzung von Dual-Stack-fähigen Geschäftsanwendungen (TCP- und TLS-Protokoll) gelegt. Implementierungsdetails von Java SE 7 (OpenJDK, Oracle und IBM) sind Teil des Vortrags.
Ich werde nicht auf die mobilen Java Profile oder Android eingehen. Und über Java 8 gibt es in dem Bereich keine Neuerungen zu vermelden.

HP-UX Development in the Cloud with HP ClOE

At $EMPLOYER we are developing and offering (Java based) COTS Enterprise Software to a wide range of customers. And while it looks like customer demand seem to concentrate on the various Linux enterprise distributions as well as Windows Server, there are of course still customers with different strategic platforms for Mission Critical applications. This includes AIX, HP-UX and Solaris which we all officially support. For a specialized ISV it is hard to have test systems for all those platforms combinations and keep up with the latest patches and Java versions. This is especially true if you do not want to maintain a whole datacenter full of test equipment. Luckily most of the system vendors offer some sort of partner program and provide opportunities to test. HP AllianceONE Partner Program Today I want to blog about the HP offering called Cloud Operating Environment (CLOE). As an HP AllianceOne Partner Program member we have access to this offering and can order virtualized HP-UX machines on a self-service portal for the purpose of porting, integrating and troubleshooting customer problems. The standard machines offered are OK for installation and compatibility testing or creating documentation for it. Simulating larger environments and doing load-tests requires different agreements. ClOE Web Page When ordering a new "Project" you have to give some details about the work you are planning to do (but I had actually never denied any request). Most of the work is related in trying out installer, shell scripts as well as Java dependencies. Remote Shell Access After ordering a Server (for example the medium configuration of HP-UX 11i v3 Data Center OE with 2 CPUs, 4GB RAM and 2 x 36GB HDD). After the server is provisioned (which takes about 12 minutes), you will receive an e-mail with the SSH key for root user. This SSH keyfile can be converted into a PuTTY .ppk file with the use of the PuttyGen Tool. The SSH access is the only open incoming network connection, so you need the SSH port forwarding feature available in PuTTY if you want to access or provide remote services. The login is a bit slow, I discovered, that it helps to put the option UseDNS no in /opt/ssh/etc/sshd_config to speed up the SSH login process. Otherwise you need to increase login timeout limits of some SFTP tools. PuTTY Login Installing Open Source Software On the first startup the system is pretty basic, there are only some Open Source goodies installed in /usr/contrib. But it is missing for example a modern shell. We can fix this with the help of the HP-UX Porting and Archive Centre:
# cd /var/tmp
# /usr/bin/ftp
Name: ftp
Password: root@ 
ftp> cd /hpux/Sysadmin/depothelper-2.00/ 
ftp> get depothelper-2.00-ia64-11.31.depot.gz
ftp> quit
# /usr/contrib/bin/gunzip depothelper-2.00-ia64-11.31.depot.gz
# /usr/sbin/swinstall -s /var/tmp/depothelper-2.00-ia64-11.31.depot depothelper
# /usr/local/bin/depothelper curl bash
# /usr/local/bin/bash
Using remote X11 If you need to run applications with a graphical user interface, you need a way to display X11 sessions. There are basically two options. The first is, you install locally (on your PC) a X Window server (like Xming) and then set the X11 DISPLAY variable to a port which is forwarded by SSH back to your desktop. This requires no additional software on HP-UX side, but is not very fast. Another problem is, that if you lose the SSH connection all GUI applications on the remote side will be terminated. So the better option is to use a X11 simulating server on the HP side and connect to it. This allows to re-attach the session. Xvnc is a X11 server which can do that. It runs on the HP server talking X11 protocol to the local clients and publishes the frame buffer via VNC protocol. On the PC you only need a VNC client and connect (via a SSH port tunnel) to the remote. Unfortunately HP offers no Xvnc out of the box. But you can buy a version from RealVNC. There is also an older 4.1.2 PARISC binary floating around on the Internet which does not require a commercial license and works fine in the Aeries emulator:
# cd /var/tmp
# export PATH=$PATH:/usr/local/bin
# curl -o vnc-4_1_2-parisc_hpux.depot.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1767k  100 1767k    0     0   158k      0  0:00:11  0:00:11 --:--:--  182k
# /usr/contrib/bin/gunzip vnc-4_1_2-parisc_hpux.depot.gz
# /usr/sbin/swinstall -s /var/tmp/vnc-4_1_2-parisc_hpux.depot VNC
# vncpasswd
# vncserver
xauth:  creating new authority file //.Xauthority

New 'aps39-92:1 (root)' desktop is aps39-92:1

Creating default startup script //.vnc/xstartup
Starting applications specified in //.vnc/xstartup
Log file is //.vnc/aps39-92:1.log
After this you need to forward port 5901 in PuTTY to localhost:5901 on remote, and can connect with a VNC Viewer on your Desktop. Expanding Filesystem Capacity The next task is, that the default filesystems are sized too small to install additional software, so you need to get access to the additional capacity. As I am not a HP-UX expert and I could not find a good tutorial on that, here is my attempt to mount the second disk and to increase the /var filesystem with the remaining LVM extends (I did not setup a LVM for the second disk to keep the two devices independent for better snapshotting):
# bdf
Filesystem          kbytes    used   avail %used Mounted on
/dev/vg00/lvol3    1048576  176080  865720   17% /
/dev/vg00/lvol1    1835008  167008 1655064    9% /stand
/dev/vg00/lvol8    4718592  899128 3794848   19% /var
/dev/vg00/lvol7    5472256 3340744 2114872   61% /usr
/dev/vg00/lvol4     524288   21200  499160    4% /tmp
/dev/vg00/lvol6    8708096 5689992 2994560   66% /opt
/dev/vg00/lvol5     106496    5792   99928    5% /home

# for i in /dev/disk/*; do diskowner -AF $i; done | grep -v ioerror

# vgdisplay -v
--- Physical volumes ---
   PV Name                     /dev/disk/disk4_p2
   PV Status                   available
   Total PE                    4495
   Free PE                     1247
   Autoswitch                  On
   Proactive Polling           On

# lvdisplay /dev/vg00/lvol8
--- Logical volumes ---
LV Name                     /dev/vg00/lvol8
LV Size (Mbytes)            4608
Current LE                  576
Allocated PE                576
# lvextend -l $[576+1247] /dev/vg00/lvol8
Logical volume "/dev/vg00/lvol8" has been successfully extended.
# lvdisplay /dev/vg00/lvol8
LV Size (Mbytes)            14584
# fsadm -b 14584m /var
fsadm: /etc/default/fs is used for determining the file system type
vxfs fsadm: V-3-23585: /dev/vg00/rlvol8 is currently 4718592 sectors - size will be increased
# mkfs -F vxfs /dev/disk/disk5
    version 6 layout
    37748736 sectors, 37748736 blocks of size 1024, log size 16384 blocks
    largefiles supported

# mkdir /u01
# echo "/dev/disk/disk5 /u01 vxfs delaylog 0 3" >> /etc/fstab
# mount /u01

# bdf
Filesystem               kbytes      used        avail      %used Mounted on
/dev/vg00/lvol3    1048576    176080     865720   17% /
/dev/vg00/lvol1    1835008    167008   1655064    9% /stand
/dev/vg00/lvol8    14934016  899496 13930104    6% /var
/dev/vg00/lvol7    5472256    3340744 2114872   61% /usr
/dev/vg00/lvol4     524288      21200       499160    4% /tmp
/dev/vg00/lvol6    8708096    5689992 2994560   66% /opt
/dev/vg00/lvol5     106496      5792           99928    5% /home
/dev/disk/disk5    37748736  26333   35364760    0% /u01
The fsadm command to online-resize the filesystem is only available in the Data Center OE, the Base Operating Environment has not the required license option "Online JFS" activated In the next article I will have a look at installing patches and Quality Packs. Update 2012-05-13: How to get Started presentation from HP: My Blog article is featured by @HPAllianceOne on Twitter.

Java Updates und der Java Installer

Aktuell geht es durch das soziale Dorf. Das Java Plug-In im Browser muss immer und immer wieder aktualisiert werden weil eine ganze Menge an wirklich kritischer Sicherheitsschwächen bekannt werden. Bei jeder dieser Update Installationen fragt die Oracle JVM ob sie nicht auch die Ask Toolbar installieren soll. Diese verunstaltet den Browser mit einer unbenutzbar schlechten Search Engine und ist auch ansonsten sehr auffällig. Aber schaut man sich den Java Installer genauer an, so ist es nicht das einzige Problem. Das Auto-Update lässt sich immer recht viel Zeit. Es hält "Recover" Kopien des kompletten Installers vor, es hat keine vernünftige Windows 7 Unterstützung (UAC Anforderung im Control Panel für Systemweite Einstellungen zum Auto Update funktionieren nicht). Außerdem ist eine Java Installation schnell mal kaputt, und der Installer bekommt es nicht immer hin die Registry aufzuräumen. Alles in allem ist man als Verwender von Applets oder WebStart (und JavaFX) sehr stark mit Support beschäftigt, weil sich immer mal wieder inkompatible Änderungen einschleichen. Ganz aktuell quäle ich mich z.B. mit Problemen beim Aufruf der JavaScript bridge auf Web Seiten herum. Aber auch die Dokumentation zum Deploy Toolkit, die Detection im Browser, ja selbst die Startup Zeiten sind immer noch (trotz neuem Plug-In und Java Kernel) verbesserungsfähig. Wenigstens gegen die Toolbar läuft jetzt eine Petition: Übrigens, kleiner Tipp. Wenn man die JRE (oder das JDK) nicht von installiert, sondern den (Offline) Installer von den Oracle Seiten nimmt, so bekommt man die Frage nach der Ask-Toolbar nicht. Dürfte alle Firmen-Admins ohne Softwareverteilung freuen.

SSL Renegotiation DoS und Java JSSE SSLServerSocket

Öffentlich zugängliche SSL/TLS Dienste bilden das Rückgrat der Internet Anwendungen und haben zugleich das Problem, dass der initiale Handshake eine CPU intensive Crypto-Operation auf dem Server erfordert. Das ist deswegen problematisch, weil der Server erst nach dieser Operation feststellen kann ob der Client den Rechenaufwand ebenfalls eingegangen ist, oder einfach nur zufällige Daten beim Schlüsselaustausch geschickt hat. Dieses Ungleichgewicht kann ein Angreifer bei einem DoS (Deny of Service) Angriff ausnutzen und wiederholt (ggf. von verschiedenen bots) eine solche SSL Verbindung anfordern. Dadurch kann es dann zu Überlastungen der Server kommen, ohne dass der Angreifer viele Ressourcen investieren müsste. Diese Form des SSL DoS lässt sich aber zum Glück einfach erkennen und dank Rate Limits in Firewalls oder SSL Accelerators auch einfach abwehren. Etwas problematischer ist es da schon, dass auch in einer bestehenden TLS Verbindung die Gegenstelle eine neue Aushandlung der Schlüsseldaten anfordern kann. Dazu sind keine von der Firewall bemerkbaren neuen Verbindungen notwendig, und auch die Handshake Records die bei SSL ausgetauscht werden sind als solche nur mit anfälliger Heuristik zu erkennen. Wenn man das nun mit der Eigenheit von RSA kombiniert, dass das entschlüsseln auf der Serverseite mehr CPU Zeit beansprucht als die Verschlüsselunsoperation, so erhält man eine neue Klasse von Angriffen auf die die deutscheinternationale Security Research Gruppe THC 2011 aufmerksam gemacht hat. Und obwohl ein dDoS Angriff auf den initialen Handshake um ein vielfaches wirkungsvoller ist hat die Veröffentlichung des Tools einige Hersteller dazu bewegt etwas dagegen zu unternehmen (die meisten schalten die Renegotiation komplett ab). Hinweis: THC spricht von Faktor 15 mehr Rechenaufwand auf dem Server als auf dem Client, dem widersprechen allerdings die Zahlen von Vincent (s.u.). Jedenfalls ist es für einen öffentlich angebotenen SSL Server wichtig die Renegotiation Anfragen der Clients zu erkennen und zu limitieren. Neben den häufig verwendeten nativen Libraries wie puressl oder openssl gibt es auch noch weitere Implementierungen, z.B. den SSLServerSocket bei Java (im Falle der JVM von Oracle mit der ehemals von Sun stammenden JSSE Implementierung). Für diese bleiben aktuell zwei Strategien: a) HandshakeListener auf jedem SSLServerSocket registrieren: dieser wird bei jedem erfolgreichen Handshake aufgerufen, er kann also mitzählen und ggf. Gegenmaßnahmen einleiten. Das hat aber einen großen Nachteil: der Listener selbst wird Asynchron in einem neuen Thread aufgerufen. Und dies auch dann wenn die Funktion im Listener nicht mehr macht als einen Zähler hochzählen. Das starten des Threads ist im Vergleich dazu viel belastender für den Server. Es sollte also vermieden werden überhaupt einen Listener zu registrieren. b) Nach dem initialen Handshake die Liste der erlaubten Ciphers auf eine leere Liste setzen. Das führt dazu, dass alle weiteren Handshake Anfragen mit einem SSL Alert abgebrochen werden. Leider gilt das allerdings auch für Handshakes die vom Server angefordert werden (z.B. wenn Client Zertifikate angefordert werden sollen) oder auch bei sehr seltenem Schlüsselaustausch (bei lange bestehenden SSL Verbindungen sinnvoll). Ich würde mir deswegen wünschen Oracle bessert hier an zwei Stellen nach: zum einen sollte man bei SSLServersockets direkt die Renegotiation die von Clients angefordert wird mittels eines Flags ignorieren/ablehnen können. Zudem macht es Sinn wenn der SSLServerSocket diese automatisch in der Anzahl begrenzen. Zum anderen sollte es möglich sein auf die Erzeugung eines Threads pro Handshake Event zu verzichten, z.b. indem der Anwender einen eigenen Executor mitgeben kann (der einen Pool hat oder aber bei sehr leichtgewichtigen Listener einfach synchron ausführt). Übrigens bietet die JSSE2 Implementierung von IBM eine Systemproperty ( an, mit der man die Renegotiation ausschalten kann. Im Falle von Oracle kann nur die unsichere Renegotiation (ohne RFC5746) verboten werden. Das hilft aber nicht gegen den DOS Angriff. Zum Weiterlesen

HTTP Header und Browser Sicherheit

Web Anwendungen werden immer essentieller und es zeigen sich immer mehr Schwachstellen und Risiken. Als Web Application Entwickler kann man einige Technologien verwenden (die mehr oder weniger verbreitet sind) um das Risiko zu reduzieren. Hier möchte ich Funktionen sammeln die als "neue" HTTP Header bereitgestellt werden:

Content-Security-Policy: (CSP) Dieser HTML5 Header erlaubt es dem Web Server der eine HTML Seite ausliefert Beschränkungen für den html/javascript code zu definieren in Bezug auf Quellen für Code (und andere Ressourcen wie Fonts und Bilder). Sinnvoll ist dieses Verfahren insbesondere in Verbindung mit Web Seiten die keinen Inline JavaScript code enthalten (nur script tags) sowie auf eval in JavaScript verzichten. Eine gute Zusammenfassung findet sich auf Leider ist die Unterstützung in den Browsern noch eingeschränkt und die verschiedenen Engines benutzen noch nicht den offiziellen Header name. [W3C Working Draft Content Security Policy 1.0]

Strict-Transport-Security: (HSTS) Ein Problem mit https (SSL/TLS) ist, wenn es nicht oder falsch verwendet wird. Mit dem HSTS header wurde eine Möglichkeit geschaffen dass Web-Seiten dem Browser der User dazu zwingen können für bestimmte URLs nur die SSL Variante zuzulassen. Der Nutzen ist ein wenig umstritten, da es zum einen noch das Henne und Ei Problem gibt und zum Anderen der Header nicht vor man-in-the-middle (MITM) Angriffen oder Phisching Angriffen mittels alternativen URLs schützen kann. Es ist aber trotzdem eine gute Idee den Header einzusetzen, vor allem wenn die Browser Hersteller dann eigene Listen erstellen und diese fest mit der Software ausliefern (und damit das Henne und Ei Problem umgehen). Die OWASP Initiative hat weiterführende Informationen. [IETF WEBSEC Draft: HTTP Strict Transport Security]

Access-Control-Allow-Origin: (CORS) Die Access-Control-Allow Header sind genau genommen kein Mechanismus um die Möglichkeiten einer Web Seite zu beschränken, sondern um diese feingranular um Zugriffe auf fremde Server (die dem zustimmen) zu erweitern. Dies erlaubt unter anderem die Vermeidung des eher problematischen JSON-P Verfahrens (das auf JavaScript eval basiert). Eine gute Beschreibung wie das Verfahren eingesetzt werden kann findet sich bei [W3C Working Draft Cross Origin Resource Sharing]

Cache-Control: no-store Diese Einstellung bietet sich vor allem für dynamisch erstellte HTML Seiten an die sensible Daten enthaten die nicht permanent auf dem Client Rechner gespeichert werden sollen. Es sollte nicht für statische ressourcen (Bilder) gesetzt werden, da dies Geschwindigkeit der Web Anwendung durch ständiges neu-übertragen eben solcher verschlechtert. [W3C RFC2616 HTTP/1.1]

Cookies: HttpOnly secure Cookies werden in Web Anwendungen unter anderem dazu eingesetzt Einstellungen oder Details auf dem Client zu erinnern oder eine Session ID zu speichern. Damit sind Cookies eine Achillesferse jedes Authorisationssystems und bedürfen besonderem Schutz (vor Einsichtname oder Auslesen). Wenn ein Server ein Cookie mittels Header anlegt, so kann er diesen Attribute mitgeben die zum einen regeln wann das Cookie zurückgeschickt wird (secure = nur wenn https methode verwendet wird) und zum anderen ob das Cookie mittels JavaScript/DOM ausgelesen werden kann, oder einfach nur für alle weiteren Zugriffe bereitsteht. Web Anwendungen sollten zum Schutz der Session Information unbedingt dieses Verfahren erzwingen: session cookies mit HttpOnly Attribut. [IETF RFC6255 HTTP State Management Mechanism] X-Frame-Option: Erlaubt Web Seiten die nicht in Frames eingesperrt werden dürfen. Dies hat den Vorteil dass es Angreifern etwas schwerer gemacht wird eine Webseite anzuzeigen die normale Funktionalitäten bereitstellt, der Angreifer aber Clicks und Eingaben abfangen kann. Außerdem bietet es auch Schutz Trittbrettfahrern die Ihre eigenen Vorstellungen von Betrieb eines Services haben. (IE8 Security Blog: Clickjacking Defense) [Quelle?]

Server:/X-Powered-By:/Via: Um es (automatisierten) Angreifern nicht so einfach zu machen sollten Header die eine schnelle Identifikation (Fingerprinting) der eingesetzten Software Version erlauben entfernt (im Fall von X-Powered-By: was gerne von JSP compilern erstellt wird) werden. Der Punkt ist eher umstritten: es reduziert die Gefahr dass bekannte Lücken einfach ausprobiert werden nicht, dafür erschwert es den Administrator und Partnern die Überprüfung der Konfiguration. Da aber viele Assessment Tools das Vorhandensein der Header anmerken ist es für einen Softwareersteller ratsam diese Header (und Versionsstrings in Fehlerseiten) konfigurierbar zu machen. In dem zusammenhang ist die Empfehlung einen falschen Versionsstring zurückzuliefern sehr kritisch zu betrachten, lieber einen unkonkreten generischen Header. Z.b. gibt der Apache httpd nur "Server: Apache" aus, wenn "ServerTokens ProductOnly" gesetzt wird. [W3C RFC2616 HTTP/1.1] Übrigens haben all diese Mechanismen bekannte (und unbekannte) Bugs in verschiedenen Browser Versionen. Nicht alle Browser unterstützen die Header im gleichen Funktionsumfang oder nach der gleichen Methode. Es macht dennoch Sinn diese einzusetzen da die Sicherheit von Web Anwendungen für größere Benutzergruppen dennoch verbessert wird. Zudem achten viele (automatisierten) Audits auf das Vorhandensein, so lässt sich mit geringem Aufwand bei der Anwendungserstelllung eine bessere Compliance erreichen.

Update 2012-11-08: Server header hinzugefügt, IE8 Security Blog artikel verlinked der X-Frame-Option erklärt.

TODO 2012-11-23: Es gibt noch die /crossdomain.xml policy files die von Flash und wohl auch dem Java Plugin beachtet werden. Diese Technologie kennt den X-Permitted-Cross-Domain-Policies: header. Dieser fehlt im Artikel noch.

Update 2014-02-16: Google setzt den X-XSS-Protection:"1; mode=block" header, welcher vom IE8 verstanden wird.

Oracle Java SE (hotspot) GC Logfile rotation

Zufällig bin ich bei den Java Flags über die Option -XX:GCLogFileSize=x gestolpert. Da ich bei Oracle (im Gegensatz zur IBM JVM) einige Diagnosefunktionen, wie z.b. rollierende GC logfiles vermisste habe ich weiter gesucht, und den RFE 6941823 gefunden, der (für Java 7U2+) beschreibt, dass das Feature jetzt vorhanden ist, und (umständlich) mit 3 Optionen konfiguriert werden muss:
C:> java -XX:+PrintFlagsFinal -version | find "GCLog"
    uintx GCLogFileSize                              = 0               {product}
    uintx NumberOfGCLogFiles                         = 0               {product}
     bool UseGCLogFileRotation                       = false           {product}
java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
Ich würde das ganze immer zusammen mit der Details und Datestamp Option verwenden:
java  -Xloggc:log/app.vgc
      -XX:GCLogFileSize=10M -XX:NumberOfGCLogFiles=10 -XX:+UseGCLogFileRotation
      -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:-PrintGCTimeStamps
Bei IBM kann man das übrigens dank kompaktem Syntax und Variablenexpansion deutlich besser machen:
$ java -Xverbosegclog:app.%Y%m%d.%H%M%S.%pid.vgc,10,10000
Und dazu liefert IBM sogar noch ein Handbuch...

Oracle JDBC Ping Database

Oracle hat im JDBC Treiber (und dem eigenen Connection Pool) jede Menge Funktionen eingebaut, die über das Verhalten von JDBC hinausgehen. Oftmals wird dies mit Performance-Vorteilen begründet. Man sollte diese Feature (nicht nur aus Kompatibilitätsgründen) nicht ungeprüft übernehmen, zum Beispiel hat JBoss AS einen OracleValidConnectionChecker, der mittels Relfection die Oracle Spezialfunktion pingDatabase() verwendet. Alternativ gibt es eine generische Implementierung, die einfach ein Statement absetzt. Die Prüfung ob eine DB connection noch funktioniert (entweder regelmässig im Hintergrund oder bei jedem ausleihen aus dem Pool) ist recht wichtig für einen stabilen Systembetrieb - aber auch sehr Performancerelevant. Mit pingDatabase würde man so vorgehen: Wenn man aber die Implementierung analysiert, so stellt man schnell fest, dass die aktuelle Version des Treibers hier nicht sonderlich effizient implementiert ist. Es wird das SQL Statement "SELECT 'X' FROM DUAL" abgesetzt. (Immerhin wird dazu vorher der Rückgabetyp deklariert: ((OracleStatement)stmt).defineColumnType (1, Types.CHAR, 1);) Es wird kein prepared statement eingesetzt, dass heisst der Datenbank Server muss bei jedem Aufruf das Statement mühsam parsen. Wenn ein Timeout angegeben wurde, so startet der JDBC Treiber bei jedem Ping einen neuen Thread. Auf die timeout Funktion sollte also auf jeden Fall verzichtet werden (Oracle hat diese Variante auch deprecated).

GUI Design

Gerade musste ich über diesen Dialog schmunzeln (es handelt sich dabei um den ClickOne .Net Installer von GitHubForWindows 1.0). Screenshot InstallerDass der Hilfetext abgeschnitten wird ist wohl ein Problem der deutschen Übersetzung in Zusammenhang mit unterschiedlichen Bildschirmauflösungen. Aber warum dieser Hinweistext überhaupt angezeigt wird ist ja schon fraglich. Der nimmt deutlich mehr Platz ein als jemals ein Domain Name lang sein könnte.

Java 7 - Probleme mit neuen JSSE Features

Mit Java 7 sind in den SSL/TLS Provider von Oracle einige neue Funktionen eingezogen. Darunter der schon lange erwartete Support für TLSv1.1 und TLSv1.2, aber auch die Unterstützung der TLS Extensions u.A. für die Server Name Indication(SNI). Letzteres wird dazu verwendet virtuelle Hosts auf einem SSL Port zu unterstützen: Bisher konnte ein SSL Server nämlich nicht wissen an welchen (der potentiell vielen) virtuellen Dienste hinter einer IP Adresse sich der SSL Client wenden will. Besonders ärgerlich ist dies im Fall von HTTP/s, dort ist es die Regel dass Hoster sehr viele Kunden-Domains hinter ein und der selben IP-Adresse betreiben. In HTTP/1.1 wird der gewünschte Servername in der Anfrage mitgegeben (Host: Header). So kann der HTTP Server entscheiden welche Webseiten er ausliefern soll. Im Falle des SSL Server Zertifikats (welches im SSL Handshake schon vor der HTTP Anfrage ausgetauscht wird), kann dies der Webserver aber nicht. Er muss raten welches Zertifikat er dem Client präsentieren soll, und das schlägt natürlich in der Regel fehl. Mit der Extension wird der Servername auch im Handshake mitgeschickt, und der Server kann sein Zertifikat passend auswählen. Problem bei der Geschichte ist: der Server darf auf eine solche Namensanfrage mit einem SSL Alert (Warning) reagieren. In dieser sagt er, dass er sich für den angefragten Host nicht zuständig fühlt. Das kommt bei aktuellen Webserver Installationen häufig vor, weil diese einfach nicht korrekt eingerichtet sind (und die modernen Browser die SNI unterstützen diese Warnung auch einfach ignorieren). Da das zurückgelieferte Default Zertifikat oftmals den richtigen Hostnamen (in einer der Attribute) enthält, klappt der gesicherte Handschlag im Alltag dennoch. Nicht jedoch mit Java 7 SSL Clients, JSSE macht daraus eine fatale Exception: handshake alert: unrecognized_name
Ich habe deswegen einen Bugreport aufgemacht, jedoch wurde dieser wieder kommentarlos geschlossen. Falls Sie nun trotz Oracle's widerstreben die Notwendigkeit haben mit einem Web Server zu kommunizieren der SNI nicht richtig eingerichtet hat, so bleiben nur 2 Möglichkeiten über: a) TLS aus der Liste der unterstützten Protokolle entfernen - mit einem SSLv3 Handshake wird kein Extension Record übertragen, und entsprechend klappt auch der Handshake (solange der Server SNI nicht benötigt). b) den SSL Socket so initialisieren, dass die SSLEngine den Host nicht kennt. Dieser sogenannte Host hint wird für mehrere Dinge verwendet, kann aber auch weggelassen werden. Erreichen kann man dies, indem man den Socket statt mit s=factory.createSocket(name, port); mit "s=factory.createSocket(); s.connect(name,port);" erzeugt. Übrigens ist dies ein ziemlich unerwartetes Verhalten: SSL mit Kerberos Authentifizierung würde nur auf die erste Art und Weise funktionieren, da hierfür die Identität des Servers bekannt sein muss. Der Punkt a) ist ein schneller Fix, kommt aber in der Praxis eigentlich nicht in Frage, da man mit Java 7 ja eher daran Interesse hat TLSv1.1 oder TLSv1.2 zu verwenden um Lücken wie z.B. den BEAST-Angriff auszuschließen. Daher bleibt es nur übrig entweder den Anwendungscode zu ändern (oder wenn man diesen nicht selbst geschrieben hat, wie im Falle einer häufig verwendeten URLConnection oder beim Apache HTTPClient) oder aber mindestens eine eigene SSLSocketFactory zu implementieren, die auf die 2-stufige Erzeugung des SSLSockets aufsetzt. Update: Ich habe in den Sourcen grade eine System Property gefunden, mit der man abschalten kann, dass der ClientHandshaker die SNI Extention sendet. Dies lässt sich als Work around gut verwenden: System.setProperty("jsse.enableSNIExtension", "false"); (muss vor der Verwendung von SSL Klassen im Programm, oder auf der Command Line gesetzt werden).

SQL Server JDBC Probleme

Von den Änderungen in Java SE 6.0 Update 29 zum Schutz vor SSL BEAST Angriffen hatte ich schon berichtet. Ein Opfer dieser Kompatibilitätsänderung sind die JDBC Treiber für den Microsoft SQL Server (jTDS und Microsoft JDBC Driver for SQL Server sind betroffen). Beim Aufbau der Verbindung (TCP) direkt mit dem jTDS Treiber kommt es zu folgendem Fehler:
java.sql.SQLException: I/O Error: Software caused connection abort: recv failed
  State  : 08S01
  Error  : 0
Und die folgende Exception wirft der Microsoft JDBC Driver for SQL Server: Connection reset
  State  : 08S01
  Error  : 0
Wenn die Treiber durch einen Connection Pool benutzt werden, oder innerhalb einer Datasource, so kann es sogar zum Hängen (wegen Endlosschleife) kommen. Eine Möglichkeit ist es, beim jTDS Treiber anzugeben, dass man kein SSL machen möchte (sollte aber eigentlich auch der default sein, laut jTDS FAQ). Dies kann man mit dem JDBC URL Property ";ssl=no" erreichen. Wenn der Server allerdings auf "Force Encryption" konfiguriert ist, so wird er dann die Logins ablehnen. Beim Microsoft Treiber würde das property "encrypt=false" lauten, dies half aber in meinen Versuchen (mit MS SQL Server 2008 R2 Express) nicht. Eine weitere Möglichkeit ist es den SSL/TLS CBC-Fix per Java System Property abzuschalten: -Djsse.enableCBCProtection=false Dies wirkt sich aber auf alle anderen SSL Verbindungen innerhalb dieser VM ebenfalls aus. Es gibt Berichte, dass dieses Problem mit JavaSE 6.0 Update 30 behoben sei, das kann ich aber weder nachvollziehen, noch lassen die ReleaseNotes darauf schließen. Ich habe mal einen Fehlerbericht bei jTDS dazu geöffnet.