Skip to content

Conservative Enterprise Linux Distributions might hurt us

The argument is clear, by conservatively patching packages (backporting fixes) systems with a Enterprise Linux contract can be held stable and secure for a long time. But to what cost (besides the support contract) this comes?

I noticed that even with the latest SUSE Linux Enterprise 15 SP5 you get a OpenSSH version which does not support hybrid post-quantum key exchanges, and even worse it has SHA1 and the old rsa-ssh key type enabled, as well as the weak DH Group14 or UMAC-64 and non-etm MACs. This openssh 8.4 clone is just not secure by default anymore.

It would be hard for a company on a Enterprise Linux trip to recompiler their own openssh (in order to get the PQC) or to harden the crypto settings. In this regard RHEL with its system wide crypto policy is a bit better, but its dubious if you can set it system wide to "FUTURE" crypto protection level just to get rid of the worst offenders.

Of course it could be argued that nobody really is endangered by those weak algorithms, especially not if the SSH connections are not used on the trusted Internet. But then again - why take the risk. We can only conclude - if you need the additional security, you will need to harden even your expensice supported enterprise systems.

Windows 11 evaluation VM unter Windows 10 Hyper–V

Es gibt ein paar Blogs zum Thema Windows 11 in einer Windows 10 Hyper-V VM (unter Windows 10) zu installieren. Falls die Systemvoraussetzungen nicht erfüllt sind, wird die folgende Meldung angezeigt:

This PC can’t run Windows 11

In dem Fall müssen die folgenden Vorraussetzungen stimmen:

  • Gen2 VM
  • mindestens 2 virtuelle Prozessoren zugewiesen
  • Secure Boot mit der Microsoft Windows Zertifizikatsvorlage
  • aktiviertes VMTPM (kann nach der Installation abgeschalten werden). Bei älteren Windows 10 (vor dem Aniversary Update) muss man dazu wohl auch den Isolation Mode aktivieren.
  • mindestens 3600MB Speicher und dynamischer Mindestspeicher (oder dynamisch abschalten für die Installation)
  • 60GB verfügbare Größe für die VHDX Disk (der 127GB default ist auch OK)

Getestet habe ich das mit einem Windows 11 Enterprise Evaluation ISO. Eine einfachere Option ist den Schnellstart Assistenten im Hyper-V Manager zu verwenden und dort die Windows 11 Developer Evaluation zu verwenden. Diese läd eine angepasste VM automatisch herunter.

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\.jnlp]

[-HKEY_CURRENT_USER\Software\Classes\.jnlp]

[-HKEY_CLASSES_ROOT\.jnlp]


[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\jnlp]

[-HKEY_CURRENT_USER\Software\Classes\jnlp]

[-HKEY_CLASSES_ROOT\jnlp]


[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\jnlps]

[-HKEY_CURRENT_USER\Software\Classes\jnlps]

[-HKEY_CLASSES_ROOT\jnlps]


[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\JNLPFile]

[-HKEY_CURRENT_USER\Software\Classes\JNLPFile]

[-HKEY_CLASSES_ROOT\JNLPFile]


[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\jnlp_auto_file]

[-HKEY_CURRENT_USER\Software\Classes\jnlp_auto_file]

[-HKEY_CLASSES_ROOT\jnlp_auto_file]


[-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]


[-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.jnlp]


; TODO the following list might not be complete, if scripting: "*_.jnlp"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts]
"JNLPFile_.jnlp"=-
"jnlp_auto_file_.jnlp"=-
"Applications\\javaws.exe_.jnlp"=-
"Applications\\javaws.bat_.jnlp"=-
"Applications\\javaws.cmd_.jnlp"=-
"Applications\\jp2launcher_.jnlp"=-
"Applications\\notepad.exe_.jnlp"=-
"Applications\\wordpad.exe_.jnlp"=-

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts]
"JNLPFile_.jnlp"=-
"jnlp_auto_file_.jnlp"=-
"Applications\\javaws.exe_.jnlp"=-
"Applications\\javaws.bat_.jnlp"=-
"Applications\\javaws.cmd_.jnlp"=-
"Applications\\jp2launcher_.jnlp"=-
"Applications\\notepad.exe_.jnlp"=-
"Applications\\wordpad.exe_.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]


[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\JavaWebStart.isInstalled]

[-HKEY_CURRENT_USER\Software\Classes\JavaWebStart.isInstalled]

[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\JavaWebStart.isInstalled.1.8.0.0]

[-HKEY_CURRENT_USER\Software\Classes\JavaWebStart.isInstalled.1.8.0.0]

Windows Server 2016 Images mittels DSIM für OpenStack vorbereiten

Für unsere OpenStack DevCloud benötigten wir Images für OpenStack die mit cloudbase-init starten. Dazu gibt es bei Cloudbase ein GIT repository das die nötigen (Powershell) Scripte bereitstellt. https://github.com/cloudbase/windows-openstack-imaging-tools Als ich dies jedoch auf einem Windows 2012 R2 Server nach Anleitung ausführen wollte was das resultierende Image nicht startfähig. Es gab Warnungen dass DSIM nicht aktuell genug ist und anscheinend wurden auch die VirtIO Treiber nicht integriert. Hier sind ein paar Schritte mit denen es dann geklappt hat: a) Git4Windows installieren. b) ein Arbeitsverzeichnis anlegen und die Image Tools dorthin auschecken:
    mkdir c:\work
    cd /d c:\work
    git clone https://github.com/cloudbase/windows-openstack-imaging-tools.git
c) Aus einer Windows Server 2016 Installations-ISO das File sources\install.wim (5,6GB) extrahieren und nach c:\work\source\install.wim legen. (Ich habe dies mit einer Volume License ISO als auch dem 180 Tage Eval ISO erfolgreich gemacht) d) Für Windows Server 2016 muss man eine DSIM Version verwenden die neuer ist. Dazu habe ich das Windows ADK für Windows 10, Version 1703 (Hardware Dev Center) auf dem Server installiert. Es gibt zwar auf der Webseite an dass dies nur für Windows 10 ist, jedoch war es in meinem Fall auch für Windows Server 2016 (Rollup CDs) notwendig. e) nach der Installation ist es Wichtig das DSIM Verzeichnis dieses neuen Kits im Pfad vor dem Windows Systemverzeichnis zu haben (da dort eine alte DSIM Version gefunden wird). Man kann `(Get-Command dism.exe).Path` verwenden um zu sehen welches DISM verwendet wird. Ich habe es mir einfach gemacht und in das verwendete Powershell script einfach einen passenden Pfad gesetzt. f) Im Beispielscript werden die RedHat VirtIO Treiber heruntergeladen, ich hatte die URL auf eine neue Version angepasst. g) Das Offline Beispielscript habe ich angepasst dass es alle Verzeichnisse unter c:\work sucht, dass es nur ein 15GB Image anlegt (und weil dieses noch transportiert und gestartet werden muss habe ich es auch als QCow2 angelegt. Hier das komplette script (auszuführen als privelegierter lokaler admin im Unterverzeichnis example): "Windows Server 2016 Images mittels DSIM für OpenStack vorbereiten" vollständig lesen

Converting IMAGE to VARBINARY(max) on SQL Server

With Microsoft SQL Server 2005 the IMAGE and (N)TEXT large data types have been deprecated in favor of VARBINARY(MAX) and (N)VARCHAR(max). We have some older database schema at customers who still use the IMAGE type so I am preparing a note to guide the migration.

There is an easy ALTER TABLE command to convert the existing tables, however it is not clear what the impact of this conversion is. So I took a deeper look. I noticed that converting a VARBINARY(max) back to IMAGE caused a lot of I/O and log usage. The other way around this seems to not happen and I can not repeat the problem.

Nevertheless I tried a few scenarios with type and parameter conversion. With IMAGE the default is to not inline data and it is configured with the 'text in row' table option which allows to define a limit of data which is inlined.

The following statement creates 4 tables with different settings for images, two heap tables and two clustered:

DROP TABLE tableHeapWithImageDefault;
DROP TABLE tableHeapWithImageInline;
DROP TABLE tableClusteredWithImageDefault;
DROP TABLE tableClusteredWithImageInline;

CREATE TABLE tableHeapWithImageDefault (cID INT identity(1,1) PRIMARY KEY NOT NULL, cImage IMAGE);
CREATE TABLE tableClusteredWithImageDefault (cID INT identity(1,1) PRIMARY KEY CLUSTERED NOT NULL, cImage IMAGE);

CREATE TABLE tableHeapWithImageInline (cID INT identity(1,1) PRIMARY KEY NOT NULL, cImage IMAGE);
EXEC sp_tableoption 'tableHeapWithImageInline', 'text in row', 'ON';

CREATE TABLE tableClusteredWithImageInline (cID INT identity(1,1) PRIMARY KEY CLUSTERED NOT NULL, cImage IMAGE);
EXEC sp_tableoption 'tableClusteredWithImageInline', 'text in row', 'ON';

If I query the details I see the following:

select name, type_desc, lob_data_space_id, text_in_row_limit, large_value_types_out_of_row from sys.tables where name like 'table%'

name                             type_desc     text_in_row_limit large_value_types_out_of_row
tableClusteredWithImageDefault   USER_TABLE    0                 0
tableClusteredWithImageInline    USER_TABLE    256               0
tableHeapWithImageDefault        USER_TABLE    0                 0
tableHeapWithImageInline         USER_TABLE    256               0

The large_value_type_out_of_row cannot be modified as long as the table does not contain a new large type.

So lets fill the tables with smaller and larger values (total blob size of (36000+180)*50000=1.6GB) for the cImage column and see what space usage the allocation units have:

while @x < 10 BEGIN
  set @x = @x + 1
  select @x
  BEGIN TRANSACTION
  declare @i int = 0;
  while @i < 5000 BEGIN
    INSERT INTO tableHeapWithImageDefault(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),5)) -- 180
    INSERT INTO tableHeapWithImageDefault(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),1000)) -- 36000
    INSERT INTO tableHeapWithImageInline(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),5))
    INSERT INTO tableHeapWithImageInline(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),1000))
    INSERT INTO tableClusteredWithImageDefault(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),5)) -- 180
    INSERT INTO tableClusteredWithImageDefault(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),1000)) -- 36000
    INSERT INTO tableClusteredWithImageInline(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),5))
    INSERT INTO tableClusteredWithImageInline(cImage) VALUES(replicate(cast(newID() as VARCHAR(max)),1000))
    SET @i = @i + 1
  END
  COMMIT TRANSACTION
END

The following query looks at the three possible types of allocation units (especially IN_ROW_DATA for data stored in the table and LOB_DATA for large external data. The third type, ROW_OVERFLOW_DATA, is not used in this example):

select o.name AS tablename, au.type_desc, CEILING(au.used_pages * 8 /1024.0) as usedMB,
       au.data_pages, au.used_pages, au.total_pages, p.partition_number, p.rows,
       au.used_pages * 8 * 1024 / p.rows as [bytes/row]
FROM sys.allocation_units AS au
  JOIN sys.partitions AS p ON au.container_id = p.hobt_id
  JOIN sys.objects AS o ON p.object_id = o.object_id
WHERE o.name like 'table%';

The result shows as expected where the data is stored (keep in mind the average length for the rows includes long and short rows).

tablename                       type_desc       usedMB    data_pages  used_pages   total_pages  rows  bytes/row
tableHeapWithImageDefault       IN_ROW_DATA          5    582         586          587          100000       48
tableHeapWithImageDefault       LOB_DATA          1778    0           227574       227611       100000    18642
tableHeapWithImageInline        IN_ROW_DATA         16    1961        1971         1971         100000      161
tableHeapWithImageInline        LOB_DATA          1758    0           225003       225035       100000    18432
tableClusteredWithImageDefault  IN_ROW_DATA          5    582         586          587          100000       48
tableClusteredWithImageDefault  LOB_DATA          1778    0           227574       227611       100000    18642
tableClusteredWithImageInline   IN_ROW_DATA         16    1961        1971         1971         100000      161
tableClusteredWithImageInline   LOB_DATA          1758    0           225003       225043       100000    18432

So now we can also record the data pages used for the allocation units so we can verify after conversion that the data has actually not been touched (idea):

select o.name, au.allocation_unit_id, au.type_desc, au.total_pages, au.first_iam_page, au.first_page, au.root_page
from sys.system_internals_allocation_units au
  JOIN sys.system_internals_partitions AS p ON au.container_id = p.partition_id
  JOIN sys.objects AS o ON p.object_id = o.object_id
WHERE o.name like 'table%';

With the following result:

name                           allocation_unit_id type_desc    total   first_iam_page first_page     root_page
tableHeapWithImageDefault      72057594197966848  IN_ROW_DATA  587     0x3DB302000100 0x3CB302000100 0x748707000100
tableHeapWithImageDefault      72057594198032384  LOB_DATA     227611  0x3BB302000100 0x38B302000100 0x38B302000100
tableHeapWithImageInline       72057594198097920  IN_ROW_DATA  1971    0xB91507000100 0x2E8B06000100 0x3FDA00000100
tableHeapWithImageInline       72057594198163456  LOB_DATA     225035  0x478707000100 0x418707000100 0x418707000100
tableClusteredWithImageDefault 72057594198228992  IN_ROW_DATA  587     0x4F8707000100 0x4E8707000100 0x768707000100
tableClusteredWithImageDefault 72057594198294528  LOB_DATA     227611  0x4D8707000100 0x4C8707000100 0x4C8707000100
tableClusteredWithImageInline  72057594198360064  IN_ROW_DATA  1971    0x558707000100 0x548707000100 0x4FDA00000100
tableClusteredWithImageInline  72057594198425600  LOB_DATA     225043  0x578707000100 0x568707000100 0x568707000100

So and now finally we can alter the columns:

SET STATISTICS TIME ON
ALTER TABLE tableHeapWithImageDefault ALTER COLUMN cImage VARBINARY(MAX);
ALTER TABLE tableHeapWithImageInline ALTER COLUMN cImage VARBINARY(MAX);
ALTER TABLE tableClusteredWithImageDefault ALTER COLUMN cImage VARBINARY(MAX);
ALTER TABLE tableClusteredWithImageInline ALTER COLUMN cImage VARBINARY(MAX);
SET STATISTICS TIME OFF

And this returns in elapsed time = 0ms with the following new data layout. What is visible at first is that it has generated empty ROW_OVERFLOW_DATA allocation units for all rows (this might indicate that the conversion could differ if the rows are (nearly) full, which is not the case for our narrow tables in the experiment).

name                           allocation_unit_id type_desc          total first_iam_page first_page     root_page
tableHeapWithImageDefault      72057594197966848  IN_ROW_DATA          587 0x3DB302000100 0x3CB302000100 0x748707000100
tableHeapWithImageDefault      72057594198032384  LOB_DATA          227611 0x3BB302000100 0x38B302000100 0x38B302000100
tableHeapWithImageDefault      72057594198491136  ROW_OVERFLOW_DATA      0 0x000000000000 0x000000000000 0x000000000000
tableHeapWithImageInline       72057594198097920  IN_ROW_DATA         1971 0xB91507000100 0x2E8B06000100 0x3FDA00000100
tableHeapWithImageInline       72057594198163456  LOB_DATA          225035 0x478707000100 0x418707000100 0x418707000100
tableHeapWithImageInline       72057594198556672  ROW_OVERFLOW_DATA      0 0x000000000000 0x000000000000 0x000000000000
tableClusteredWithImageDefault 72057594198228992  IN_ROW_DATA          587 0x4F8707000100 0x4E8707000100 0x768707000100
tableClusteredWithImageDefault 72057594198294528  LOB_DATA          227611 0x4D8707000100 0x4C8707000100 0x4C8707000100
tableClusteredWithImageDefault 72057594198622208  ROW_OVERFLOW_DATA      0 0x000000000000 0x000000000000 0x000000000000
tableClusteredWithImageInline  72057594198360064  IN_ROW_DATA         1971 0x558707000100 0x548707000100 0x4FDA00000100
tableClusteredWithImageInline  72057594198425600  LOB_DATA          225043 0x578707000100 0x568707000100 0x568707000100
tableClusteredWithImageInline  72057594198687744  ROW_OVERFLOW_DATA     0  0x000000000000 0x000000000000 0x000000000000

And on the second glance we notice that neither the AU unit ID nor the page addresses has changed for any of the IN_ROW_DATA or LOB_DATA. So this means the change from IMAGE to VARBINARY(max) is low impact for the tested cases.

When turning on the out_of_row setting for the table(Heap/Clustered)WithDefault tables (which had no inline data before) the situation does not change. The elapsed time = 0ms and the result is unchanged:

SET STATISTICS TIME ON
exec sp_tableoption 'tableHeapWithImageDefault', 'large value types out of row', '1';
exec sp_tableoption 'tableClusteredWithImageDefault', 'large value types out of row', '1';
SET STATISTICS TIME OFF

Results in this (removed overflows):

name                           allocation_unit_id type_desc   total_pages first_iam_page first_page     root_page
tableHeapWithImageDefault      72057594197966848  IN_ROW_DATA 587         0x3DB302000100 0x3CB302000100 0x748707000100
tableHeapWithImageDefault      72057594198032384  LOB_DATA    227611      0x3BB302000100 0x38B302000100 0x38B302000100
tableClusteredWithImageDefault 72057594198228992  IN_ROW_DATA 587         0x4F8707000100 0x4E8707000100 0x768707000100
tableClusteredWithImageDefault 72057594198294528  LOB_DATA    227611      0x4D8707000100 0x4C8707000100 0x4C8707000100

Running the same option (elasped 15ms) change on the previously inlined-enabled tables:

SET STATISTICS TIME ON
exec sp_tableoption 'tableClusteredWithImageInline', 'large value types out of row', '1';
exec sp_tableoption 'tableHeapWithImageInline', 'large value types out of row', '1';
SET STATISTICS TIME OFF

With the unchanged pages:

name                          allocation_unit_id type_desc   total_pages first_iam_page first_page     root_page
tableHeapWithImageInline      72057594198097920  IN_ROW_DATA 1971        0xB91507000100 0x2E8B06000100 0x3FDA00000100
tableHeapWithImageInline      72057594198163456  LOB_DATA    225035      0x478707000100 0x418707000100 0x418707000100
tableClusteredWithImageInline 72057594198360064  IN_ROW_DATA 1971        0x558707000100 0x548707000100 0x4FDA00000100
tableClusteredWithImageInline 72057594198425600  LOB_DATA    225043      0x578707000100 0x568707000100 0x568707000100

So even that change has no impact. According to MSDN the reason for this is that the LOBs are only changed when updated. So this is also quite safe.

The same should be true for the other way around, however I have seen cases, where it took much longer with lots of logfile usage. Maybe you have an idea how I can recreate this scenario?

where - Das find des armen Windows Mannes

Ich gebe zu, ich benutze Windows. Sogar relativ gerne. Das liegt vor allem daran, dass ich es satt bin an meinem Desktop herumzuspielen (und keinen Zugang zu Macs habe). Aber als Linux developer kenne ich natürlich auch die Stärke einer Shell und der CLI tools. Deswegen habe ich auch immer ein Cygwin installiert so dass ich tools with find und grep nutzen kann. Ich finde die Windows Powershell sehr interessant und mächtig, bin es aber nicht gewohnt diese zu nutzen.

Deswegen lande ich auch oft in einem klassischen Windows Prompt (und ich nutze ConEmu um das erträglich zu machen). Ein Windows command das man auf jeden Fall kennen sollte ist where: es ist eine Kobination aus find, which und locate und erlaubt die Suche nach Files in Verzeichnissen oder Suchpfaden:


Suche ausführbare Befehle

Ohne zusätzliche Option durchsucht where das aktuelle Verzeichnis und den PATH. Das wird dazu benutzt um Befehle zu finden, dabei sind Suchmuster möglich:

C:\WINDOWS\system32>where java*
C:\Windows\System32\javaws.exe
C:\ProgramData\Oracle\Java\javapath\java.exe
C:\ProgramData\Oracle\Java\javapath\javaw.exe
C:\ProgramData\Oracle\Java\javapath\javaws.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\java-rmi.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\java.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\javac.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\javadoc.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\javah.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\javap.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\javaw.exe

Rekursive Suche

Um Dateien mit einem Suchmuster unterhalb eines Pfades zu suchen verwendet man die /r option. Mit der /t option gibt where dann gleich noch ein paar Datei Details aus:

C:\>where /T /R c:\Windows\System32 *.cmd
    843   30.10.2015   08:17:50  c:\Windows\System32\onlinesetup.cmd
    199   30.10.2015   08:17:43  c:\Windows\System32\winrm.cmd

Scripting Hilfe

Zudem gibt es noch seziellen Such Syntax, so kann ich alle in einer Environment Variable enthaltenen Verzeichnisse durchsuchen:

C:\>where $PATH:java
C:\ProgramData\Oracle\Java\javapath\java.exe
C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.111-1\bin\java.exe

Oder aber in einer Liste von Verzeichnissen suchen:

C:\>where "C:\Windows\system32;C:\Windows\SysWOW64":mode
C:\Windows\System32\mode.com
C:\Windows\SysWOW64\mode.com

4096bit RSA Keys mit OpenPGP 2.1 Smartcards

Ich bereite die Einführung einer PGP PKI vor. Dazu habe ich mir ein paar Crypto Hardware Artikel beschafft und werde hier meine Erfahrungen verbloggen. Anfangen möchte ich mit den OpenPGP 2.1 Cards (von Zeitcontrol via Cryptoshop). Die Karten lassen sich direkt in dem connected Smart Card reader meines betagten Dell Latitude E6510 unter Windows 7 ansprechen. Dabei kommt der eingebaute Broadcom Reader zum Einsatz:

c:\Program Files\OpenSC Project\OpenSC\tools>opensc-tool -v -l
# Detected readers (pcsc)
Nr.  Card  Features  Name
0    Yes             Broadcom Corp Contacted SmartCard 0
     3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c CryptoStick v1.2 (OpenPGP v2.0)

c:\Program Files (x86)\GNU\GnuPG>gpg2.exe --card-status
Application ID ...: D27600012401020100050000xxxx0000
Version ..........: 2.1
Manufacturer .....: ZeitControl
Serial number ....: 0000xxxx
Name of cardholder: Bernd Eckenfels
Language prefs ...: en
Sex ..............: männlich

Läßt sich also ohne Probleme sowohl mit OpenSC 0.15.0 Win64 sowie GnuPG 2.0.29 (GPG2Win 2.3.0) ansprechen. Ich konnte mit dieser Kombination auch 3072bit RSA Schlüssel erstellen und nutzen. Problematisch wird es allerdings wenn man 4096bit RSA Schlüssel nutzen möchte. Die Karten bieten diese Option an (deswegen habe ich sie auch ausgewählt). Leider kann der eingebaute Reader keine Extended-APDU, somit schlägt der Versuch fehl entsprechende Keys zu erzeugen (eigentlich sollte er die Keys erzeugen können aber es sieht danach aus dass die anschliessende Übertragung fehlschlägt:

tools> openpgp-tool -r 0 -v --verify CHV3 --pin 12345678 --gen-key 1 --key-len 3072
Connecting to card in reader Broadcom Corp Contacted SmartCard 0...
Using card driver OpenPGP card.
Fingerprint:
9DA81881C7139920120ABBF8C50BD95C 9A7C7684

tools> openpgp-tool -r 0 -v --verify CHV3 --pin 12345678 --gen-key 1 --key-len 4096
Connecting to card in reader Broadcom Corp Contacted SmartCard 0...
Using card driver OpenPGP card.
... multiple minutes later...
Failed to generate key. Error Transmit failed.

c:\Program Files (x86)\GNU\GnuPG> gpg2.exe --card-status
Application ID ...: D27600012401020100050000xxxx0000
Version ..........: 2.1
Manufacturer .....: ZeitControl
Serial number ....: 0000xxxx
Name of cardholder: Bernd Eckenfels
Language prefs ...: en
Sex ..............: männlich
URL of public key : [nicht gesetzt]
Login data .......: [nicht gesetzt]
Signature PIN ....: nicht zwingend
Key attributes ...: 4096R 4096R 4096R
Max. PIN lengths .: 32 32 32
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
...

Ach ja übrigens, noch ein paar Anmerkungen: Es ist wichtig gpg2.exe (nicht gpg.exe) zu verwenden, bei PGP4Win werden beide binaries in unterschiedlichen Versionen parallel installiert. Bei gpg2 muss man darauf achten dass es auf einige Hintergrundprozesse angewiesen ist (pinentry und scdaemon). Das kann schon mal zu Verwirrungen führen (z.b. Unter älteren Linux Varianten kann sich wohl ein Gnome daemon als pgp-agent ausgeben und dann smartcard befehle abweisen. Mit gpg-connect-agent -v und dem Befehl SCD GETINFO version lässt sich prüfen welcher Agent verwendt wird. In meinem Setup hängt sich auch die GPA artenverwaltng auch, habe noch nicht herausgefunden woran dies liegt.

Mit der Verwendung eines externen USB Readers der Extended APDU unterstützt (Gemalto IDBridge Ct710) lässt sich der 4096bit Key erstellen und nutzen:

c:\Program Files\OpenSC Project\OpenSC\tools>opensc-tool -v -l
# Detected readers (pcsc)
Nr.  Card  Features  Name
0    Yes             Broadcom Corp Contacted SmartCard 0
     failed: Card not present
1    Yes   PIN pad   Gemalto IDBridge CT7xx 0
     3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c CryptoStick v1.2 (OpenPGP v2.0)

c:> echo debug-all > C:\users\eckenfel\AppData\Roaming\GnuPG\scdaemon.conf
c:> echo log file c:/temp/scdaemon.log >> C:\users\eckenfel\AppData\Roaming\GnuPG\scdaemon.conf
c:> echo reader-port "Gemalto IDBridge CT7xx 0" >> C:\users\eckenfel\AppData\Roaming\GnuPG\scdaemon.conf

c:\Program Files (x86)\GNU\GnuPG> gpg2 --card-edit -v

Application ID ...: D27600012401020100050000xxxx0000
Version ..........: 2.1
...
gpg/card> admin
gpg/card> generate
Make off-card backup of encryption key? (Y/n) n

gpg: NOTE: keys are already stored on the card!

Replace existing keys? (y/N) y
What keysize do you want for the Signature key? (3072) 4096
The card will now be re-configured to generate a key of 4096 bits
What keysize do you want for the Encryption key? (3072) 4096
The card will now be re-configured to generate a key of 4096 bits
What keysize do you want for the Authentication key? (3072) 4096
The card will now be re-configured to generate a key of 4096 bits
...
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
gpg: writing self signature
gpg: RSA/SHA256 signature from: "CE551545 [?]"
gpg: writing key binding signature
gpg: RSA/SHA256 signature from: "CE551545 [?]"
gpg: writing key binding signature
gpg: RSA/SHA256 signature from: "CE551545 [?]"
gpg: writing key binding signature
gpg: RSA/SHA256 signature from: "CE551545 [?]"
gpg: writing key binding signature
gpg: RSA/SHA256 signature from: "CE551545 [?]"
gpg: writing public key to `C:/Users/eckenfel/AppData/Roaming/gnupg/pubring.gpg'
gpg: writing secret key stub to `C:/Users/eckenfel/AppData/Roaming/gnupg/secring.gpg'
gpg: using PGP trust model
gpg: key CE551545 marked as ultimately trusted
public and secret key created and signed.

pub   4096R/CE551545 2016-06-05 [expires: 2016-06-06]
      Key fingerprint = 1C93 E95E 0486 C55C 0762  3629 20CC CA05 CE55 1545
uid       [ultimate] Bernd Eckenfels (test) <test@test>
sub   4096R/F738D4AE 2016-06-05 [expires: 2016-06-06]
sub   4096R/309D4991 2016-06-05 [expires: 2016-06-06]

Das Erstellen von 3 Schlüsseln (inkl. Signaturen, ohne Encryption Key backup - d.h. auch auf der Karte erstellt) mit 4096 bit dauert (stark schwankend) 30 Minuten, die selbe Prozedur mit 3072 bit Schlüssel erfolgt in 8 Minuten.

Bei der Erstellung der Schlüssel gibt pgp2 wenige Zwischenschritte aus, man kann das ganze aber im scdaemon logfile nachvollziehen. Wie oben zu sehen war es auch notwendig den Klartext Namen des externen Card Readers zu konfigurieren, scdaemon hat reader-port 1 oder reader-port 32769 (trotz Neustart) schlichtweg ignoriert. Auch wenn der richtige Reader verwendet wird scheint die Debug Ausgabe nur den Slot des Readers aber nicht den gewählten Reader auszugeben:

scdaemon[14808] Handhabungsroutine f³r fd -1 gestartet
scdaemon[14808] detected reader `Broadcom Corp Contacted SmartCard 0'
scdaemon[14808] detected reader `Gemalto IDBridge CT7xx 0'
scdaemon[14808] detected reader `'
scdaemon[14808] reader slot 0: not connected

Bei der Key-Erstellung hat gnupg2 das externe Keypad des Gemalto readers für die PIN Eingabe (noch) nicht benutzt.

Password hashing in Oxwall (PHP password_hash)

While planning a new social network installation for the customer community of my employer I came across Oxwall as a possible solution. It is an Web application on the PHP/MySQL stack which allows self-hosted communities. It runs with a small footprint on about any web server hosting platform and provides a simple Forum, Chat, Event Stream, User Blogs and Profiles and similar functions. This is a very good fit to our needs (as we have to migrate away from the old Mixxt hosted community platform).

First we had a look a Ning, but as a Company located in Germany it is always complicated to host user-facing systems on US systems. Especially since EU and German data protection regulations for personal identifiable items might apply in this area. So if we cannot find a good German social network provider, we will consider hosting the application in our own data centers.

But before we invite out valuable customers to register on such a platform we do need to make sure their data stays safe. This is a business oriented social platform, so we do not really expect critical top secret information, but we do want to protect the most sensitive information, the users passwords. If you look at recent events in the media this is more than important. Thousands of cracked accounts and millions of leaked password hashes are available on the net. Even technology giants like Sony are not safe from being hacked. So it is absolutely important we protect our users and therefore our reputation - even for a simple application like a forum.

Oxwall password hashing

For this reason I reviewed a few security aspects of Oxwall (1.7.2), and want to talk here especially about how it stores user passwords.

Oxwall stores password hashes in the ow_base_user table in the password column as a 64-digit lower-case hex string. It represents the SHA-256 hash of the static OW_STATIC_SALT pepper concatenated with the user's password. This is all done in the hash_password($password) function, the relevant code looks like:

./ow_system_plugins/base/bol/user_service.php:884:        return hash('sha256', OW_PASSWORD_SALT . $password);

OW_PASSWORD_SALT (which is actually a static pepper) is a 13 digit hex string generated at installation time and stored in ow_includes/config.php. It is generated with the PHP uniqid() function (current time in milliseconds in hex). This is not a very strong secret, but you can of course overwrite it (keep in mind, this will invalidate all existing password hashes).

In order to verify a password Oxwall uses the isValidPassword($userid, $password) function, which will SHA-256 hash the provided password (one iteration) and compare it to the stored hash with PHP's === operator (which is not a constant time string comparision operation).

Oxwall does by the way enforce a minimum and maximum password length. The PASSWORD_MAX_LENGTH = 15 and PASSWORD_MIN_LENGTH = 4.

Oxwall Password Protection Evaluation

There are a number of problems with this:

By using a static salt (better known as pepper) all users who have the same password have the same hash (in one Oxwall installation). It also means an attacker can prepare a bit better for attacks, as they can check all user passwords with the same pre-compiled table. A globally unique salt for each password hash should be preferred to this.

By not using a format specifier algorithm flexibility is limited. Oxwall would have to test old and new algorithms (or guess based on the length of the encrypted string).

Although SHA-2 is more complex to calculate compared to DES, SHA-1 or MD5 it is still a comparable fast operation which can be easily optimized in hardware. This is no longer regarded as a safe protection. Instead it is better to have an iterated algorithm (and optionally memory complexity).

The current function for verifying the password hash is not implemented with a constant-time comparison method. This means it could leak some information on the password hashes to remote attackers.

If you compare this with other PHP applications it is not a bad implementation, but it is surely not state of the art anymore. Oxwall really should aim for a modern implementation.

There is absolutely no reason to limit the length of a password to 15 characters. It can be even longer than the hash function. I would remove that limit or at least increase it to a sane value like 100 (or 128 if you want to look techy :). Oxwall should invest some more work into encouraging users to have safe passwords. This can include hooks to check for bad passwords. The current minimum of 4 characters is quite low (and it would be good if one can configure it without changing the code).

Modern PHP password hashing

Luckily PHP 5.5 is providing a password_hash() function. And if you cannot wait for it, there is a compatibility library from Anthony Ferrara which can be used on 5.3. The function supports generating random salts, it uses BCRYPT algorithm by default and uses the standard $2y$... hash format.

I feel it is a bit unfortunate, that PHP does not support an additional pepper argument (in the default implementation). It argues, that there is no official analysis how much additional protection it brings.

The RFC comments, that it is possible to encrypt the hashes in your database (with a pepper). This would add the protection, but it generates additional work. The pepper Oxwall is using is rather low entropy, so I think it is best to just drop it. This will also reduce the risk of losing this value.

Oxwall should use the password_hash function, not specify salt or cost parameters and implement the rehash checks, to make sure it automatically rehashes passwords whenever PHP modifies the settings.

SSL Verkehr überwachen

Im letzten Artikel (zur Poodle Lücke) habe ich die klare Empfehlung ausgesprochen SSL 3.0 (und 2.0 sowieso) abzuschalten, wenn es nicht mehr benötigt wird. Hier stellt sich natürlich sofort die Frage: "gibt es noch kritische Verwender von SSL3 in meinem Netz".

Für die Unternehmens-IT kann das eine relativ komplizierte Angelegenheit werden die Owner aller Anwendungen zu befragen. Die Anwendungen zu ermitteln (einfach die Liste der freigeschaltenen Ports in der Firewall durchgehen) ist noch relativ einfach, ob aber die Befragten die Antwort kennen (oder die Frage verstehen) ist eine ganz andere Sache.

SSL Log im Apache aktivieren

Dazu kommt noch das Problem dass in jeder Anwendung die Art und Weise wie man die verwendeten SSL Protokolle sehen kann unterschiedlich ist. Teilweise ist die Funktion nicht aktiviert oder lässt sich nur mit vielen zusätzlichen Debug Meldungen einschalten.

Bei einem Apache httpd ist es eigentlich zwingend das erweiterte SSL Log Format "combinedssl" immer zu verwenden:

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{SSL_PROTOCOL}x %{SSL_CIPHER}x" combinedssl
CustomLog /var/log/httpd/access.log combinedssl

Es ist praktisch, wenn die SSL/TLS Terminierung an möglichst wenigen Stellen erfolgt. Zum Beispiel einen Cluster aus Reverse Proxies oder Load Balancern. Und natürlich sollte für diese Dienste ein entsprechendes Log bereitstehen.

tls-hello-dump

Aber nicht immer gibt es diese Möglichkeit (für alle Anwendungen). Insbesondere nicht bei Client Verbindungen. Es kann von daher notwendig sein auf Netzwerkebene den Datenstrom zu überwachen und eine Statistik zu erstellen (oder die IP Addressen der notorischen SSL3 Verwender manuell nach zu püfen. Es könnte ja auch der unternehmenskritische Bestellfluss des größten Auftraggebers sein). Ich vermute mal ein gutes Network Intrusion Detection System (NIDS) kann diese Statistiken liefern. Ist eine solche Klassifizierung des Traffics aber nicht gegeben, so beginnt die Suche nach einfachen Tools.

Mit tcpdump oder vergleichbaren Sniffern muss man recht viel selbst bauen. Auch ssldump ist hier recht sperrig (und auch nicht sehr aktuell). Das einfachste Tool was ich bisher gefunden habe ist tls-hello-dump von Georg Lukas. Es schreibt pro Handshake 2 Zeilen, eine für die vorgeschlagenen Ciphers des Clients (ClientHello) und eine für die vom Server angenommenen Parameter (ServerHello). Es setzt auf libpcap auf, kann somit auf Linux/FreeBSD einfach eingesetzt werden. Es kann sowohl Verbindungen an das lokale System überwachen als auch Netzwerktraffic (solange der Host auf dem tls-hello-dump läuft am Netzwerkport der in PROMISC geschaltet ist alle Daten sieht).

Hier als Beispiel ein Chrome38/Apache2.2 Handshake sowie ein (unbeantworteter) openssl s_client -ssl3 -connect versuch:

109.192.117.164 195.49.138.57   TLSv1 ClientHello TLSv1.2 :CC14:CC13:
  C02B:C02F:009E:C00A:C009:C013:C014:C007:C011:0033:0032:0039:009C:
  002F:0035:000A:0005:0004:

195.49.138.57   109.192.117.164 TLSv1.2 ServerHello TLSv1.2 cipher C02F

109.192.117.164 195.49.138.57   SSLv3 ClientHello SSLv3 :C014:C00A:C022:
  C021:0039:0038:0088:0087:C00F:C005:0035:0084:C012:C008:C01C:C01B:
  0016:0013:C00D:C003:000A:C013:C009:C01F:C01E:0033:0032:009A:0099:
  0045:0044:C00E:C004:002F:0096:0041:0007:C011:C007:C00C:C002:0005:
  0004:0015:0012:0009:0014:0011:0008:0006:0003:00FF:

Und das ganze noch einmal aufbereitet mit dem ebenfalls beigelegten sed script, welches aus den Hex-codes der Ciphers lesbare Namen macht.

109.192.117.164 195.49.138.57   TLSv1 ClientHello TLSv1.2 :TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
  TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
  TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
  TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
  TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:TLS_ECDHE_RSA_WITH_RC4_128_SHA:TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
  TLS_DHE_DSS_WITH_AES_128_CBC_SHA:TLS_DHE_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_AES_128_GCM_SHA256:
  TLS_RSA_WITH_AES_128_CBC_SHA:TLS_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_3DES_EDE_CBC_SHA:
  TLS_RSA_WITH_RC4_128_SHA:TLS_RSA_WITH_RC4_128_MD5:

195.49.138.57   109.192.117.164 TLSv1.2 ServerHello TLSv1.2 cipher TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

109.192.117.164 195.49.138.57   SSLv3 ClientHello SSLv3 :TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
  TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
  TLS_DHE_RSA_WITH_AES_256_CBC_SHA:TLS_DHE_DSS_WITH_AES_256_CBC_SHA:TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
  TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
  TLS_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
  TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
  TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
  TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:TLS_RSA_WITH_3DES_EDE_CBC_SHA:
  TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
  TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:TLS_DHE_RSA_WITH_AES_128_CBC_SHA:TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
  TLS_DHE_RSA_WITH_SEED_CBC_SHA:TLS_DHE_DSS_WITH_SEED_CBC_SHA:TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
  TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
  TLS_RSA_WITH_AES_128_CBC_SHA:TLS_RSA_WITH_SEED_CBC_SHA:TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:TLS_RSA_WITH_IDEA_CBC_SHA:
  TLS_ECDHE_RSA_WITH_RC4_128_SHA:TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:TLS_ECDH_RSA_WITH_RC4_128_SHA:
  TLS_ECDH_ECDSA_WITH_RC4_128_SHA:TLS_RSA_WITH_RC4_128_SHA:TLS_RSA_WITH_RC4_128_MD5:TLS_DHE_RSA_WITH_DES_CBC_SHA:
  TLS_DHE_DSS_WITH_DES_CBC_SHA:TLS_RSA_WITH_DES_CBC_SHA:TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
  TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:TLS_RSA_EXPORT_WITH_DES40_CBC_SHA:TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
  TLS_RSA_EXPORT_WITH_RC4_40_MD5:TLS_EMPTY_RENEGOTIATION_INFO_SCSV:

Ich hab ein paar Patches beigesteuert die es etwas robuster machen, man muss sich aber vor Augen halten dass das Tool nur sehr einfache Handshakes versteht (diese müssen im Anfang der gefilterten IPv4 Pakete stehen). Schon ein TLS Alert (Warnung: unrecognized_name) verhindert, dass das ServerHello erkannt wird. Außerdem ist aktuell der Parser sehr ungenau was die Überprüfung der Paketformate angeht, es bietet sich also an das Tool nicht als Root auf einem kritischen System laufen zu lassen sondern vielmehr den Traffic mittels tcpdump -w file zu speichern und dann Offline auszuwerten (mit einem User ohne weitere Rechte).

Es sollte auch darauf hingewiesen werden dass es sich hier nur um die Handshakes handelt, Clients die viele erfolglose Verbindungen aufbauen sind in den Zahlen entsprechend überräpresentiert. Wenn eine persistente HTTPS connection tausende Anfragen verarbeitet steht diese nur einmal in der Liste. Ebenso sieht man nicht, ob ein Client, wenn er SSL3 offeriert bekam dieses auch akzeptiert (oder ablehnt).

tls-hello-dump kann nicht mit STARTLS (wie es z.B. in SMTP und IMAP Verwendung findet) umgehen. Das liegt hauptsächlich daran dass es sich nicht damit befasst die gefilterten Pakete zu TCP Strömen zusammenzusetzen. Das macht das Tool leichtgewichtig aber eben auch beschränkt. ssldump wäre für diesen Fall die bessere Wahl.

Compilieren und starten

tls-hello-dump steht auf GitHub als source code zur Verfügung (habe noch kein Binärpaket gesehen) und sollte auf jedem Linux Rechner mit entsprechenden Entwicklungspaketen übersetzbar sein:

git clone https://github.com/ge0rg/tls-hello-dump.git
cd tls-hello-dump/
make clean all
FILTER="tcp port 443 and tcp[tcp[12]/16*4]=22 and (tcp[tcp[12]/16*4+5]=1 or tcp[tcp[12]/16*4+5]=2)"
sudo tcpdump -p -s 1518 -i eth0 -w handshakes.pcap "$FILTER"
./tls-hello-dump handshakes.pcap | sed -f readable.sed 

Oder für die Mutigen: "sudo ./tls-hello-dump eth0 https | sed -f readable.sed"

Der Poodle bellt (SSL Sicherheitsschwäche)

Wieder einmal macht eine Sicherheitslücke mit einem klangvollen Namen die Runde. Poodle ist eine Schwachstelle in SSL 3.0, die sich aber auch auf neuere Clients auswirkt. Ausgelöst wurde das ganze durch ein Paper der Google Sicherheitsforscher Bodo Möller, Thai Duong und Krzysztof Kotowicz. Warum Sites wie Twitter mit der Abschaltung von SSL 3.0 reagieren möchte ich hier kurz darstellen.

Zuerst einmal eine Feststellung: SSL 3.0 (das Secure Socket Layer Protokoll in der Version 3.0 von 1996, nachträglich in RFC6101 dokumentiert) ist (trotz der Versionsnummer) der Vorgänger von TLSv1, TLS 1.1 oder dem aktuellen TLS 1.2 (RFC5246). Es hat eine Reihe von Sicherheitsproblemen und Unschönheiten und sollte schon länger nicht mehr verwendet werden.

Die Poodle Lücke hat das nur bestätigt: Durch eine ungeschickte Verwendung von Füll-Bytes bei dem CBC Modus (verwendet für DES, 3DES und RC2) kann ein Angreifer der mehrere SSL Anfragen beeinflussen und abhören kann, auf den Inhalt schließen. Diese Schwachstelle ist ähnlich zu der bereits vor einiger Zeit bekannt gewordenen BEAST Lücke.

Es sind verschiedene Situationen denkbar, bei denen diese Lücke ausgenutzt werden kann, aber im Web Browser Umfeld ist es dank JavaScript und Userinteraktion am Einfachsten.

Für SSL 3.0 kann dies nicht behoben werden (und selbst wenn wäre es einfacher direkt TLS 1.2 zu verwenden das die Lücke nicht hat). Bei Clients und Server die SSL 3.0 noch einsetzen könnte man die betroffenen CBC Ciphers abschalten, das ist aber leider keine sehr attraktive Lösung da sonst nur noch die eher unsichere RC4 Stromchiffre verwendet werden könnte.

SSL 3.0 muss sterben

Ohne jetzt nun näher zu bewerten wie einfach oder wie schwer die Lücke in welcher Situation ausgenutzt werden kann sind sich eigentlich alle einig: SSL 3.0 sollte nicht mehr verwendet werden und kann in Clients und Servern endlich abgeschaltet werden. Dies nimmt somit auch schon die Empfehlung vorweg und den Weg gehen alle mehr oder weniger schnell.

Ob man die Unterstützung für SSL 3.0 abschalten kann hängt vor allem damit zusammen ob alle potentiellen Gegenstellen ein gemeinsames neueres Protokoll sprechen. Als Web Server Betreiber stellt sich die Situation recht einfach dar: alle Clients außer Internet Explorer 6 auf einem Windows XP (und älter) können mindestens TLSv1, die meisten auch schon 1.1 oder 1.2 (wobei es nicht alle angeschaltet haben).

Internet Explorer 6 muss brennen

Aus einer ganzen Reihe von Gründen ist es eigentlich nicht mehr wünschenswert IE 6 zu benutzen, und schon gar nicht zu fördern. Da es selbst für Anwender die auf XP festsitzen Alternativen gibt (darunter Firefox und IE 7-8) sollte man inzwischen keinen Gedanken verschwenden und SSL 3.0 in öffentlichen Web Servern sofort abschalten.

CloudFlare, ein CDN und Cloud Security Dienstleister der weltweit viele Websites schützt und ausliefert hat Statistiken veröffentlicht, sie werden SSL 3.0 abschalten:

Across our network, 0.09% of all traffic is SSLv3. For HTTPS traffic, 0.65% across our network uses SSLv3. The good news is most of that traffic is actually attack traffic and some minor crawlers. For real visitor traffic, today 3.12% of CloudFlare's total SSL traffic comes from Windows XP users. Of that, 1.12% Windows XP users connected using SSLv3. In other words, even on an out-of-date operating system, 98.88% Windows XP users connected using TLSv1.0+ — which is not vulnerable to this vulnerability.

Wenn man weg geht von dem reinen Webbrowser/Server Umfeld, so ist die Unterstützung von neueren Protokollen nicht ganz so klar. Allerdings ist TLSv1 bereits in Java SE 1.4 enthalten, und so dürfte die Anzahl der Endpunkte die kein TLS können sich auf spezielle Embedded Hardware (und falsch konfigurierte Software) beschränken. Im System-to-System oder MFT Umfeld ist das Risiko der POODLE Lücke deutlich geringer (weil der interaktive Einfluss fehlt), aber auch hier wäre es ratsam und Risikofrei SSL 3.0 einfach abzuschalten.

SSL Fallback

Wenn sowohl Client als auch Server das TLS Protokoll sprechen so können Sie dies beim Verbindungsaufbau erkennen, und die gemeinsam gesprochene beste Version aushandeln. Dank Überprüfung dieses Handshakes ist es einem Angreifer auch nicht möglich der jeweils anderen Seite eine niedrigere Version vorzugaukeln.

Allerdings gibt es bei der Sache einen Haken: da manche Clients wenn ein Verbindungsaufbau mit einem TLS Handshake nicht klappt das ganze erneut mit einer älteren Version versuchen (out-of-band protocol fallback). Ein Angreifer muss damit nicht den ersten Handshake ändern (was TLS erkennt), sondern nur aufhalten (in der Praxis passiert dies sogar bei Überlastung ganz ohne Angreifer, was ebenso nicht erwünscht ist). Wenn der Client einen erneuten Versuch unternimmt und dabei nur SSL 3.0 ankündigt, so gelingt der Handschlag zwischen Client und Server mit einer unnötig alten (und unsicheren) Protokoll Version.

Dieser Downgrade Angriff ist Bestandteil der POODLE Lücke (wenn neuere Client und Server verwendet werden). Zur Verhinderung davon gibt es mehrere Möglichkeiten:

  • Clients verzichten auf den Fallback handshake und verlassen sich auf die TLS Versionslogik (dazu gehört zum Beispiel Java, wenn der Entwickler die Logik nicht selbst implementiert hat)
  • Auf Clients und Servern unsichere Protokollversionen gar nicht anbieten (SSL 3.0 abschalten)
  • Ein neuer Mechanismus bei dem ein Client der einen kompatiblen Handschlag versucht eine Markierung (die nicht entfernt werden kann) mitschickt, dass es ein Kompatibilitätsversuch ist. Server die von dieser Markierung wissen würden den Handschlag dann ablehnen (wenn sie eine neuere Version sprechen).

Letzteres wird TLS_FALLBACK_SCSV genannt, es handelt sich um eine sogenannte Signalling Cipher, die aktuell in Diskussion ist (und Google Chrome sendet dies schon einige Zeit, Firefox ab Version 35). Damit diese funktionieren kann, müssen aber die Server diese auch kennen. Die Erweiterung ist umstritten, denn für den Fall SSL 3.0 ist ein abschalten einfacher, und für zukünftige TLS Protokoll sollten die Clients einen Fallback einfach gar nicht durchführen.

TLSv1

So positiv wie es zu sehen ist, dass sehr viele Lösungen mindestens TLSv1 sprechen, so ist dies auch ein deutliches Warnzeichen: TLSv1 ist auch schon stark angekratzt. Die meisten wirklich wünschenswerten Verbesserungen sind erst in TLSv1.1 oder 1.2 zu finden. Schon heute können Sicherheitsbewusste Anbieter TLSv1 abschalten (Der Mozilla Server Side TLS Security Guide nennt dies „Modern Compatibility“). Es ist entsprechend anzuraten gleich jetzt zu evaluieren ob man auch gleich TLSv1 verbannen möchte und sicherzustellen dass TLSv1.2 angeboten wird (und so konfiguriert ist, dass es Perfect Forward Secrecy unterstützt und auf Zertifikaten basiert die mit SHA2 statt SHA1 oder gar MD5 signiert sind).

Zum weiterlesen

Heartbleed, woher kommt der Name

Wieder einmal macht SSL/TLS Schlagzeilen, dieses mal durch eine Sicherheitslücke in OpenSSL mit der Bezeichnung CVE-2014-0160 und dem Spitznamen "Heartbleed". Aber woher kommt dieser Name?

Der Name setzt sich zusammen aus dem Begriff "Heartbeat" also Herzschlag und "to bleed" also bluten - oder wie in diesem Fall ausbluten.

Der technische Titel ist "TLS heartbeat read overrun". Angefangen hat alles mit einer TLS Erweiterung die im RFC 6520 "TLS/DTLS Heartbeat Extension" (Februar 2012) definiert ist. Diese erlaubt es in TLS Verbindungen (oder bei DTLS Paketen) zusätzlich zu den normalen verschlüsselten Sitzungsdaten auch beliebig große herzschlag Nachrichten auszutauschen. Diese sind benannt nach Ihrer Funktion: dem Nachweis dass die Gegenseite noch am Leben ist und erreichbar ist. Im Falle von DTLS wird die Möglichkeit zu bestimmen wie viele Daten gesendet (und wieder zurückgesendet werden) dazu benutzt um auszumessen was die maximale Paketgröße ist die aktuell verwendet werden kann.

Die Erweiterung an sich ist keine schlechte Idee (auch wenn natürlich alle Erweiterungen immer ein Risiko darstellen). Die Verwendung der Heartbeat Extension wird gegenseitig beim Verbindungsaufbau ausgehandelt, und nicht jede Software untersützt diese Funktion bereits. Mit einem Patch hielt diese Funktion dann auch vor 2 Jahren Einzug in OpenSSL 1.0.1.

Und dabei wurde, wie es schon zu erwarten ist ein Programmierfehler begangen: DTLS und TLS sind leider nicht die einfachsten Protokolle. Daten werden in Records übertragen, deren Länge am Anfang steht. Darin finden sich dann Nachrichten unterschiedlichen Types - wie zB die Heartbeat Anfrage und Antwort. Da die Nachrichten selbst unterschiedliche Länge haben wird innerhalb der Nachrichtenstruktur nochmal übertragen wie lange die Nachricht ist. Was der Code jetzt fälschlicherweise tut ist, er kopiert die heartbeat Daten der Anfrage in das Antwortpaket aber verlässt sich dabei auf die Längenangabe der Anfrage-Nachricht ohne vorher zu prüfen ob diese in den übertragenen SSL Record gepasst hätte.

Damit kopiert die Funktion nicht nur Daten des Angreifers sondern eben auch beliebige Speicherinhalte dahinter in die Antwortnachricht. Diese wird dann sauber und sicher verschlüsselt und zurückgesendet. Mit jeder Heartbeat Antwort können so knapp 64kb Daten aus dem Innersten des Prozesses an den Angreifer gesendet werden, der Prozess blutet damit quasi aus. Betroffen sind alle OpenSSL Versionen zwischen 1.0.1,1.0.1a-f sowie 1.0.2-betas.

Behoben ist das Problem in 1.0.1g (und zukünftig 1.0.2) nachdem sowohl Google als auch Codenomicon den Bug entdeckt und an das OpenSSL Team gemeldet haben (Wenn man der sehr vollständigen Beschreibung auf heartbleed.com glauben darf).

Was in den Daten steht die der Angreifer sich SSL gesichert herunterladen kann ist schwer zu sagen. Man sollte aber auf jeden Fall davon ausgehen dass der SSL Schlüssel des Servers sowie zufällige Sitzungsdaten davon betroffen sind. (Update 2014-04-26: inzwischen ist es klar (Rubin Xu), warum Teile des Schlüssels immer wieder im Heap auftauchen.)

BTW: wer es genau wissen will: XKCD#1354 :)

Search Meetup Karlsruhe

Search User Group KA LogoLetzte Woche fand das erste Meetup der neuen Search Meetup Gruppe in der IHK statt. Die Auftaktveranstaltung war gesponsort* von Exensio. Florian und Tobias die Organisatoren konnten einige Search-Begeisterte aus dem Raum Karlsruhe zusammenbekommen um die zukünftigen Themen des Meetups zu besprechen. Tobias ielt den ersten Kurzvortrag (den ich leider verpasst hatte). Anschliessend gab es noch angeregte Diskussionen um zukünftige Themen. Solange aus den unterschiedlichsten Unternehmen die rund um Karlsruhe Such Technologien (wie Lucene, Elastic Search oder Solr) einsetze Vortragsangebote kommen, sollten auch künftige Meetups spannend werden.

Twitter: @SearchKA
Meetup Gruppe: Search Meetup Karlsruhe

 * Es scheint garnicht zu einfach sein in der Technologie Region Karlsruhe kostenfrei Räumlichkeiten für UserGroups zu erhalten. Hier sollten sich die diversen Marketing-Arme der Stadt und "High Tech Industrie" mal was überlegen.

Oracle Container Datenbank

Seit einiger Zeit gibt es eine Neuauflage des SQL Datenbankklassikers von Oracle. Unter der Bezeichnung Oracle 12c (C wie Cloud) und der internen Versionsnummer 12.1 stehen einige neue Funktionen bereit.

Multitenant Architectur (CDB) 

Mit der 12c Release hat Oracle eine neue Multitenant Architektur eingeführt, die es erlaubt eigenständig verwaltbare Datenbanken in einer gemeinsamen Datenbankinstanz zu betreiben. Damit verbunden sind einige neue Features wie der Transport der gesamten Datenbank (nicht nur einzelner Tablespaces) von einer Instanz in eine andere. Es erlaubt ebenfalls das bereitstellen von Clones der Datenbank für Entwicklung und QA. Das Hauptargument ist jedoch die Selbstverwaltung durch beschränken der Sichtbarkeit der Datenbankobjekte auf den jeweiligen Untermieter - alles was man für die Konsolidierung von Datenbanken so braucht.

Damit zieht Oracle mit dem Microsoft SQL Server gleich, bei dem Datenbanken ja schon länger eigenständige Objekte (mit eigenen Usern) waren. Allerdings wirkt es wie gewohnt bei Oracle etwas aufgesetzter. So gibt es die normalen Systemuser die in allen PDB gelten (z.B. DBSNMP) aber für weitere User empfiehlt Oracle eine Namenskonvention "C##USERNAME" für gemeinsam benutzte (Common) User.

Meiner Erfahrung nach heißt es Vorsicht beim Umstieg auf 12c (wenn die CDB aktiviert ist). Die neue Multitenant Architektur ist zwar praktisch, DBA sind es aber nicht gewöhnt. So ist es z.B. wichtig vorhandene User+Rollen Definitionsscripte entweder im richtigen Container aufzurufen. Entweder man benutzt einen Common DB User und ändert den Session Context:

ALTER SESSION SET CONTAINER = 'PDBTEST'; 

Dann muss man sich aber auch regelmäßig vergewissern, welches die gerade aktive PDB ist:

SELECT SYS_CONTEXT('USERENV', 'CON_NAME') FROM DUAL;

Oder man legt entsprechende Administrationuser in jeder PDB separat an. Dazu muss man dann auch einen TNS Connect String verwenden der auf einen Datenbank Service verweist, welcher der jeweiligen PDB zugeordnet ist. Wenn eine neue PDB angelegt wurde, wird auch ein neuer Service mit PDB Name + DB_Domain registriert:

> SELECT NAME,PDB,CON_ID from GV$SERVICES;
NAME                         PDB                  CON_ID
---------------------- -------------------------- ----------
pdbtest.eckenfels.net   PDBTEST                   4
pdborcl.eckenfels.net   PDBORCL                   3
orcl.eckenfels.net      CDB$ROOT                  1
SYS$BACKGROUND          CDB$ROOT                  1
SYS$USERS               CDB$ROOT                  1  

Die Arbeit mit CDBs macht Oracle nicht gerade einfach. Wenn man eine neue Container Datenbank mit dem Datebank Konfigurationsassistent (oder CREATE Statement) anlegt, so wird diese Datenbank bei einem Instanzstart nicht automatisch geöffnet. Man muss sich also mit einem Systemtrigger behelfen. Das hätte man wirklich komfortabler lösen können (und wenn Oracle in einer nächsten Version eine Lösung dafür anbietet wird man sich mit den Triggern wieder beschäftigen müssen).

Das ist übrigens ein sehr ärgerliches Problem, der Oracle SQL Developer gibt zwar in der Console eine entsprechende Fehlermeldung aus:

> ALTER SESSION SET container = PDBORCL;
session SET geändert.
> CREATE ROLE "NORMAL_USER";
Fehler beim Start in Zeile 1 in Befehl:
CREATE ROLE "NORMAL_USER"
Fehler bei Befehlszeile:1 Spalte:1
Fehlerbericht:
SQL-Fehler: ORA-01109: Datenbank nicht geöffnet
01109. 00000 -  "database not open"
*Cause:    A command was attempted that requires the database to be open.
*Action:   Open the database and try the command again
> ALTER PLUGGABLE DATABASE PDBORCL OPEN READ WRITE;
pluggable DATABASE geändert.
> CREATE ROLE "NORMAL_USER";
role "NORMAL_USER" erstellt.

Wenn man das ganze allerdings im SQL Developer macht, so wird die Meldung durch ein Fehler Pop-Up mit unverständlicher Fehlermeldung verdeckt:

Bei der Ausführung des angeforderten Vorgangs ist ein Fehler aufgetreten:
ORA-06550: Zeile 3, Spalte 35:
PLS-00201: Bezeichner 'DBMS_SQL.OPEN_CURSOR' muss deklariert werden
ORA-06550: Zeile 3, Spalte 19:
PL/SQL: Item ignored
ORA-06550: Zeile 13, Spalte 26:
PLS-00320: Die Typ-Deklaration dieses Ausdruckes ist unvollständig oder fehlerhaft
ORA-06550: Zeile 13, Spalte 9:
PL/SQL: Statement ignored
ORA-06550: Zeile 16, Spalte 17:
PLS-00201: Bezeichner 'DBMS_SQL.LAST_ERROR_POSITION' muss deklariert werden
ORA-06550: Zeile 16, Spalte 5:
PL/SQL: Statement ignored
ORA-06550: Zeile 18, Spalte 34:
PLS-00320: Die Typ-Deklaration dieses Ausdruckes ist unvollständig oder fehlerhaft
ORA-06550: Zeile 18, Spalte 11:
PL/SQL: Statement ignored
06550. 00000 -  "line %s, column %s:\n%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:
Herstellercode 6550

Eine gute Zusammenfassung aller CDB spezifischen Punkte findet sich im Oracle Database Administrator's Guide im Kapitel 40 Administering a CDB with SQL*Plus

Enterprise Manager Database Express

Eine neue Funktion ist das Enterprise Manager Database Express Modul. Statt einen abgespeckten Enterprise Manager (Database Control) mit eigenem Prozess und Ablaufumgebung zu installieren kann man das in der Datenbank eingebaute Express Modul verwenden. Dieses basiert auf der AppEx Infrastruktur. Das als Performance Hub getaufte Dashboard profitiert dann auch mit den Offline abspeicherbaren HTML Reports. Ansonsten ist die Funktionalität allerdings sehr eingeschränkt. Es gibt z.B. keine DDL Funktionen zum Anlegen oder verwalten von Tabellen.

EM DB Express unterstützt die Verwendung von Plugable Databases dahingehend, dass man für jeden Tenant ein eigener http oder https Listener (Port) anlegen kann. Wenn man sich dann nicht im Root Container anmeldet, so sieht man dann erst die jeweils in der PDB angelegten User (Und leider nur dann).

> select dbms_xdb_config.gethttpsport from dual;
GETHTTPSPORT
------------
5500
> alter session set container = PDBTEST;
> select dbms_xdb_config.gethttpsport from dual;
GETHTTPSPORT
------------
> exec DBMS_XDB_CONFIG.SETHTTPSPORT(5502);
> select dbms_xdb_config.gethttpsport from dual;
GETHTTPSPORT
------------
5502

Wenn man sich mit einer PDB verbunden hat (in meinem Beispiel https://server:5502/em/) so wird in der Titelzeile des EM Express links oben nicht nur die SID, sondern auch den Namen der PDB angezeigt. Ebenso enthält die Userliste nicht mehr (nur) die Gemeinsamen User:

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.