Skip to content

The Lampster Bluetooth Details (BLE)

Yellow super-hero figurine in A-pose with tractor lamp head as desk lamp, shining red/blue RGB light.

Only 6 years after my wife gifted me with a Lampster from Kickstarter as X-Mass present the device finally arrived.

I may post a review here, but today I just wanted to write down what I have found out about its BLE control.

I used the LightBlue iPad App to discover services and tried out a few settings.

Lampster has one service with the UUID B8EC7DF5-DB9A-B2Bf-F4A9-93DBA36C7BC2 This service has multiple properties, the following I reversed:
Manufacturer Name: "The Lampster, the licitatie"
Model Number: "LA-2017B"
Serial Number: "15-....-6555"
Hardware Revision: 100B
Firmware: 10
Software Revision: 0.1.0

First Characteristic(?) 01FF5553-

Property 1 (Mode?) 01FF5554-BA5E-F4EE-5CA1-EB1E5E4B1CE0
1 Byte: On/Off in White and/or RGB Mode
Bit 7 = on/off, Bit 6 = White, Bit 5 = RGB, Bit 4 = Off on Disconnect (0-3 always zero)
0xA0 = RGB, 0xC0 = White, 0xE0 = Both, 0x20 = Off, next RGB, 0x40 = Off, next White, 0x60 = off, next both

Property 2 (White Levels?) 01FF5556-BA5E-F4EE-5CA1-EB1E5E4B1CE0 (if mode = C0)
2 Byte 0xwwcc ww = 0x00-0x64 warm white (percentage), cc = 0x00 - 0x64 cold white (percentage)

Property 3 (RGB) 01FF5559-BA5E-F4EE-5CA1-EB1E5E4B1CE0 (if mode = A0)
3 Byte 0xrrggbb rr = 0x00 -0x64 red, gg = 0x00 - 0x64 green, bb = 0x00 - 0x64 blue (percentage)
There are 5 more properties, maybe they are related to timer and demo mode? Second Characteristic(?) 01FF5550- write only (indicate, without response) properties 01FF5551- and 01FF5552-. Both seems to disconnect and turn off the light (until reconnect).

TCP Keepalive bei Oracle Servern

Zu einer TCP Verbindung zwischen zwei Endpunkten gehört auch, sich darüber einig zu sein ob eine Verbindung noch besteht oder nicht. Wartet eine Seite auf weitere Daten und ist die andere Seite inzwischen verschwunden, so sieht die wartende Seite nichts von dem TCP Verbindungsabbruch.

Dies geschieht nur, wenn die TCP Verbindung unsauber abgebrochen wurde. Ursachen einer solchen unsauberen Verbindungsbeendigung könnte Stromausfall, Hardwarecrash, Kernelpanic oder das unerwartete entfernen der IP-Addresse (wie z.B. bei Failover-Clustern) sein. Ebenso kann ein Netzwerkausfall dazu führen dass die Beendigung einer Verbindung nur auf einer Seite bekannt wird. Manche Firewalls die eine Verbindung nur eine bestimmte Zeit zulassen können hier auch zu Problemen führen.

Übrigens, ein Beenden der Anwendung (auch mit kill -9) alleine führt nicht zu so einer Situation. Denn das Betriebsystem schliesst alle Sockets eines beendeten Prozesses sauber.

Welche Methoden stehen für ein Endpunkt zur Verfügung einseitig bestehende Verbindungen mit Lese-Operationen zu erkennen?

  1. sie implementiert einen Lese-Timeout. Dies hat den Vorteil dass auch hängende Gegenstellen erkannt werden. Problem dabei ist aber die Dauer des Timouts. Ist dieser zu kurz gewählt so werden Verbindungen vorzeitig beendet wenn die Gegenstelle langsam antwortet. Um dies zu verhindern muss der Timeout recht groß gewählt werden. Bei Anwendungen die nur sehr sporadisch Anfragen bekommen und deren Verbindungen lange Zeit idle sein sollen lassen sich deswegen Timeouts fast garnicht verwenden.
  2. die Seite die lesend auf dem Socket wartet muss regelmäßig Daten senden oder erwarten. Dies wird gemeinhin als Heartbeat bezeichnet. Dieser hat aber den Nachteil dass zum einen das eingesetzte Protokoll solche Heartbeat Nachrichten erlauben (wenn ich auf eine Antwort warte und stattdessen eine Heartbeat Nachricht erhalte). Die Implementierung wird dadurch komplexer, braucht z.B. mehrere Threads oder Asynchrone methoden. Zudem hat das Versenden von Heartbeat Nachrichten auf einer Verbindung den Nachteil dass im Falle einer Unerreichbarkeit der TCP retry Mechanismus zuschlägt, der ggf. Relativ lange benötigen kann bis er Probleme feststellt,
  3. eine weitaus unproblematischere Möglichkeit ist der TCP Keepalive Mechanismus. Dieser wird entgegen seinem Namen hauptsächlich dazu verwendet abgerissene Connections zu erkennen. Der Mechanismus funktioniert so, dass auf der Seite einer TCP Verbindung auf der Keepalive aktiv ist (ggf. auf beiden) regelmäßig geprüft wird ob eine erfolgreiche Kommunikation stattfand, oder der Socket idle ist. Wenn er Idle ist weil nichts versendet oder empfangen wurde so sendet der Keepalive Mechanismus ein leeres TCP Paket. Die Anwendungen selbst bekommen davon nichts mit, nur der TCP Stack der Gegenseite bestätigt das Paket. in Fehler beim versenden führt zu einem Abbruch der Verbindung, genauso wie das ausbleiben von einer Antwort nach eingestellter Wiederholung.

Um das in der Praxis zu sehen habe ich eine Oracle 12c mit „Dead Client Detection“ konfiguriert. Dabei handelt es sich um eine Option welche den TCP Keepalive auf eingehenden Client Connections anschaltet (und auch gleich auf den angegebenen Wert in Minuten konfiguriert). Das ist deswegen ganz praktisch weil der default Wert unter Linux bei 2h liegt und damit nicht nur relativ nutzlos ist, sondern auch zum testen sehr langatmig.

Wenn ich jetzt eine Oracle Verbindung aufbaue, so werden nur Daten an den Server gesendet wenn ich im Client einen SQL Befehl absetze. Mit netstat oder dem neueren ss (socket statistics) kann man den Zustand der TCP timer einer einzelnen Verbindung betrachten. In diesem Fall verwende ich die -o option gibt die timer Information mit aus.

[oracle@bernd-db centos]$ netstat -tnpo
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name     Timer
tcp        0      0 10.14.100.82:22         10.0.103.42:53111       ESTABLISHED -   on (0.22/0/0)
tcp        0      0 127.0.0.1:58466         127.0.0.1:1521          ESTABLISHED 1207/ora_lreg_orcl   off (0.00/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (22.76/0/0)

Um nur die relevante Verbindung fortwährend zu betrachten verwende ich die -c option. Mittels grep beschränke ich mich auf eine Verbindung. (Dieses Filtern kann mit ss effizienter gemacht werden, aber auf meinem Testsystem gibt es keine große Anzahl an Verbindungen) Parallel dazu läuft folgender tcpdump Befehl

[oracle@bernd-db centos]$ sudo tcpdump  -ttt -nn --dont-verify-checksums -U -v host 10.0.103.42 and port 54076 &
[1] 18819
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

[oracle@bernd-db centos]$ netstat -tnpoc 2>/dev/null | grep 10.0.103.42:54076
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (8.66/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (7.65/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (6.64/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (5.64/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (4.63/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (3.62/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (2.61/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (1.61/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (0.60/0/0)

 00:00:00.000000 IP (tos 0x0, ttl 64, id 2241, offset 0, flags [DF], proto TCP (6), length 40)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [.], ack 3621868956, win 1184, length 0
 00:00:00.001712 IP (tos 0x0, ttl 124, id 10289, offset 0, flags [DF], proto TCP (6), length 40)
    10.0.103.42.54076 > 10.14.100.82.1521: Flags [.], ack 1, win 256, length 0

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (5.61/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (4.60/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (3.59/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (2.59/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (1.58/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (0.57/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (53.93/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (52.92/0/0)
...

Was ist passiert? In diesem Beispiel sieht man die letzten 8 Sekunden des herabzählenden keepalive timers. In der ganzen Minute davor wurden keine Daten ausgetauscht, und so sendet der Kernel beim Ablauf des Timers ein leeres ACK Paket (Flags [.] length 0) und wartet 6 weitere Sekunden bevor er prüft ob dieses angekommen ist. Wenn ja setzt er einen neuen Timer auf (60s abzüglich der bereits gewarteten knapp 6s)

Der Keepalive Timer wird bei Oracle 12.2 mit der Option sqlnet.expire_time=1 in ${ORACLE_HOME}/network/admin/sqlnet.ora auf eine Minute konfiguriert. Das Wiederholungsintervall von 6s wird durch Oracle fest vorgegeben.

Wenn ich jetzt 18 Sekunden bevor der Timer abläuft eine Client Anfrage stelle, so ändert sich die Ausgabe etwas:

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (19.71/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (18.70/0/0)

 00:01:41.833122 IP (tos 0x0, ttl 124, id 10335, offset 0, flags [DF], proto TCP (6), length 61)
    10.0.103.42.54076 > 10.14.100.82.1521: Flags [P.], seq 1:22, ack 1, win 256, length 21
 00:00:00.000306 IP (tos 0x0, ttl 64, id 2242, offset 0, flags [DF], proto TCP (6), length 55)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [P.], seq 1:16, ack 22, win 1184, length 15
 00:00:00.007188 IP (tos 0x0, ttl 124, id 10337, offset 0, flags [DF], proto TCP (6), length 53)
    10.0.103.42.54076 > 10.14.100.82.1521: Flags [P.], seq 22:35, ack 16, win 256, length 13
 00:00:00.000077 IP (tos 0x0, ttl 64, id 2243, offset 0, flags [DF], proto TCP (6), length 55)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [P.], seq 16:31, ack 35, win 1184, length 15
 00:00:00.041996 IP (tos 0x0, ttl 124, id 10339, offset 0, flags [DF], proto TCP (6), length 40)
    10.0.103.42.54076 > 10.14.100.82.1521: Flags [.], ack 31, win 256, length 0

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (17.69/0/0)

 00:00:00.881484 IP (tos 0x0, ttl 124, id 10342, offset 0, flags [DF], proto TCP (6), length 139)
    10.0.103.42.54076 > 10.14.100.82.1521: Flags [P.], seq 35:134, ack 31, win 256, length 99
 00:00:00.000610 IP (tos 0x0, ttl 64, id 2244, offset 0, flags [DF], proto TCP (6), length 234)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [P.], seq 31:225, ack 134, win 1184, length 194
 00:00:00.003408 IP (tos 0x0, ttl 124, id 10344, offset 0, flags [DF], proto TCP (6), length 61)
    10.0.103.42.54076 > 10.14.100.82.1521: Flags [P.], seq 134:155, ack 225, win 255, length 21
 00:00:00.000238 IP (tos 0x0, ttl 64, id 2245, offset 0, flags [DF], proto TCP (6), length 55)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [P.], seq 225:240, ack 155, win 1184, length 15
 00:00:00.041847 IP (tos 0x0, ttl 124, id 10346, offset 0, flags [DF], proto TCP (6), length 40)
    10.0.103.42.54076 > 10.14.100.82.1521: Flags [.], ack 240, win 255, length 0

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (16.69/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (15.68/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (14.67/0/0)
...
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (7.62/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (6.61/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (5.61/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (4.60/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (3.59/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (2.58/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (1.57/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (0.57/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (42.22/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (41.21/0/0)

Es ist aber wieder zu sehen dass der Timer erst auf 0 zählt, dann erkennt dass ein Austausch vor 18 Sekunden stattfand, und deswegen einen neuen Timer mit der verbleibenden Restzeit von 40 Sekunden aufzieht aber kein ACK Paket versendet.

Um jetzt zu sehen was passiert wenn der Client keine Antworten versendet installiere ich einfach eine Firewall Regel die alle Pakete verwirft (DROP nicht REJECT):

[oracle@bernd-db centos]$ iptables -I INPUT -p tcp -s 10.0.103.42 --sport 54076 -j DROP

Im Folgenden habe ich die Antwort-Pakete (die tcpdump noch sieht) entfernt:

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (2.31/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (1.30/0/0)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (0.30/0/0)

 00:02:00.190237 IP (tos 0x0, ttl 64, id 2258, offset 0, flags [DF], proto TCP (6), length 40)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [.], ack 155, win 1184, length 0

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (5.31/0/1)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (4.30/0/1)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (3.29/0/1)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (2.28/0/1)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (1.27/0/1)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (0.27/0/1)

 00:00:06.014269 IP (tos 0x0, ttl 64, id 2259, offset 0, flags [DF], proto TCP (6), length 40)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [.], ack 155, win 1184, length 0

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (5.27/0/2)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (4.27/0/2)
...
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (1.04/0/9)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (0.03/0/9)

 00:00:06.014242 IP (tos 0x0, ttl 64, id 2267, offset 0, flags [DF], proto TCP (6), length 40)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [.], ack 155, win 1184, length 0

tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (5.04/0/10)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (4.04/0/10)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (3.03/0/10)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (2.02/0/10)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (1.01/0/10)
tcp6       0      0 10.14.100.82:1521       10.0.103.42:54076       ESTABLISHED 18470/oracleorcl     keepalive (0.01/0/10)

 00:00:06.013377 IP (tos 0x0, ttl 64, id 2268, offset 0, flags [DF], proto TCP (6), length 40)
    10.14.100.82.1521 > 10.0.103.42.54076: Flags [R.], seq 240, ack 155, win 1184, length 0

Wenn dieses mal der Timer abläuft ohne Datenaustausch, so wird das Keepalive Paket vom Server versendet und der Timer auf 6s gestellt. Da das Paket aber nicht durchkommt, so kam auch nach 6s keine Antwort zurück. Der Kernel auf Serverseite erkennt dies, zählt den Keepalive Zähler hoch (letzte Stelle innerhalb von timer (time/retries/keepalives)), sendet noch ein Paket und wartet erneut 6s.

Oracle hat den Retry auf 10 Probes konfiguriert, entsprechend wird bei der letzten Runde (nach einer Minue) kein Keepalive Paket mehr gesendet sondern die Connection resettet. (Flags [R.] - In meinem Fall bekommt der Client das RST nicht, wegen der Firewall). Hätte ich die Firewall Regel vor Ablauf der 10 retries entfernt, so hätte der normale Keepalive Ablauf wieder weiter gemacht.

Falls die Anwendung keine Konfiguration von TCP Keepalives pro Socket zulässt, so werden folgende Linux Kernel Werte verwendet:

[oracle@bernd-db oracle]$ sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9

Es gibt noch eine Besonderheit, wenn der Keepalive wiederholt wird, aber gleichzeitig auch Daten versendet werden, so wird der Keepalive Mechanismus ausgesetzt, stattdessen versucht der normale Retry Mechanismus Daten zu versenden, aber das soll Gegenstand eines anderen Blogposts sein.

Facebook messages mit Jitsi nutzen

Facebook bietet (noch) an auf Chat Messages mittels XMPP clients zuzugreifen. Jitsi ist ein Messenger, Sprach- und Video-Konferenz Client, der unter anderem für Facebook Chats schon vorbereitet ist.Als Java Anwendung kann er unter Windows, Linux und OS X genutzt werden.

Um Facebook in Jitsi zu nutzen fügt man ein neues Konto mit dem Typ des Netzwerks  "Facebook" aus, und muss dann nur noch Benutzername und Passwort eingeben. Allerdings sollte man beachten, vorher einen Nutzernamen in den Facebook Einstellungen angelegt zu haben.

Ist das eigene Facebook Konto mit einem Codegenerator oder einer Anmeldebestätigung gesichert (was sehr zu empfehlen ist), so lehnt der Facebook XMPP Server die Anmeldung ohne weitere Begründung ab (evtl. wird eine SMS mit XMPP Login Passwort verschickt). In diesem Fall muss ein spezielles Anwendungspasswort erstellt werden. Das ist schon alleine deswegen sinnvoll weil diese Passwörter die auf Rechnern hinterlegt sind getrennt von Konto Passwort verwaltet werden können.

In der Facebook Titelleiste auf den Menu Pfeil clicken, dann auf Einstellungen gehen. In der folgenden Seite den Benutzername prüfen und ggf anlegen.


Dann in der linken Menuleiste auf Sicherheit wechseln, dort "Anwendungspasswörter" bearbeiten, hinter dem "generieren" Link findet sich ein kleiner Dialog.

Dieser fragt nach dem Facebook Anmeldepasswort und dann kann man einen frei gewählten Namen für das Passwort eingeben kann (z.B. "Jitsi auf Notebook").

Dann wird ein zufälliges Passwort angezeigt, dieses kann man in Jitsi verwenden.

 In Jitsi ist zu beachten dass der Screen in der Deutschen Version ein Feld namens "E-Mail" hat, dort ist aber der Benutzername einzugeben (Issue#155).

Ein Vorteil der Anwendungspasswörter ist es übrigens, dass man diese widerrufen kann (falls jemand Hoffnungen hatte).

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"

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.

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 html5rocks.com. 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 html5rocks.com. [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.

E-Mail versenden von Amazon EC2 (SES mit Exim)

Wie ich bereits berichtet hatte kann es für einen Anwender der IaaS Cloud Amazon EC2 Sinn ergeben den Simple Email Service (SES) von Amazon zu verwenden um E-Mails zu versenden. Dabei kann man entweder die Amazon API/Tools verwenden, oder inzwischen auch die Mails per smtps an den Amazon Smarthost liefern. Amazon selbst bietet nur einen SMTP-SSL Server auf Port 465 an. Die meisten MTAs können dieses Protokoll nicht mehr sprechen und ziehen STARTTLS vor, deswegen empfiehlt Amazon in seiner Anleitung die Verwendung von stunnel als zwischengeschaltenen Proxy. Diese Indirektion ist aber unschön, und kann mit einer aktuellen Exim Version vermieden werden. Ab der Version 4.77 unterstützt der SMTP Transport von Exim auch das smtps Protokoll (für ausgehende Verbindungen). Mit folgenden Exim Einstellungen (Auszug) kann also ein EC2 Linux Rechner direkt an den Amazon Dienst die E-Mail ausliefern (wichtig: die Envelop-From und From: Addresse der versendeten Mails müssen in der Liste der verifizierten SES Absender sein. Als Empfänger kommen nur dann beliebige E-Mail Addressen in Frage, wenn man diese Funktion bei Amazon explizit freischalten hat lassen. 1. Alle Mails an dem Amazon Server leiten (dnslookup entfernen):
smarthost:
  driver = manualroute
  domains = ! +local_domains
  transport = remote_smtps
  route_data = email-smtp.us-east-1.amazonaws.com
  no_more
2. Einen neuen SMTP/SSL Transport definieren:
remote_smtps:
  driver = smtp
  protocol = smtps
  #port = 465
  hosts_require_auth = *
  hosts_require_tls = *
3. Zusätzlich einen Authenticator mit dem SES User/Passwort definieren:
client_login:
  driver = plaintext
  public_name = LOGIN
  client_send = : xxxxI6CIH2YIWSNxxxxx : Agcq00AEvA2ZDiQHNrDvTEODE3FWa1rxxxxx
Ich habe das ganze unter Amazon Linux mit Exim 4.77 aus dem AltCent Repository getestet:
wget http://centos.alt.ru/repository/centos/6/i386/exim-4.77-1.el6.i686.rpm
rpm -i exim-4.77-1.el6.i686.rpm
Es bietet sich an Exim so zu konfigurieren, dass nur auf localhost auf dem Submission Port E-Mails angenommen werden:
daemon_smtp_ports = 587
local_interfaces = 127.0.0.1
Dies stellt sicher, dass der Dienst nicht für Spam oder Sicherheitsangriffe ausgenutzt werden kann.

Eckpunktepapier der BNetzA zu Smart Grids und Markets

Die Bundesnetzagentur hat ein Eckpunktepapier zum Thema Smart Grids und Markets veröffentlicht. Damit will die Bundesbehörde
durch dieses Papier eine intensivere Diskussion der Thematik anstoßen zu können und die Veränderungen des Energieversorgungssystems weiter voran zu bringen. Dieses Eckpunktepapier soll die Diskussion zu dieser Thematik weiter befördern und besser strukturieren.
Das "Eckpunktepapier der Bundesnetzagentur zu den Aspekten des sich verändernden Energieversorgungssystems" enthält Leitgedanken und Begriffsdefinitionen und ist schon aus diesem Grund sehr lesenswert. Ob die einzelnen Thesen (Abnahme des Gesamtenergieverbrauchs) nun zutreffen wird sich zeigen, von einer Zunahme der Stromabnahme wird aber ausgegangen. Auch bei intelligenteren Verbrauchern (da zunehmend andere Energieformen verdrängt werden, was angesichts der Endlichkeit von fossilen Energieträgern nur logisch ist). Das Paper baut darauf, dass Verbraucher sich über Marktsignale (Preise) steuern lassen. Das wird sich zeigen wie gut das funktioniert... Ein wenig sehr optimistisch dürfte die Annahme der Transaktionskostensenkung bei "kleinteiliger Interaktion" sein (Fehlschläge im Smartmeter Markt wie in den Niederlanden oder Großbritannien deuten ja eher darauf hin, dass es sinnvoll sein kann alternative Lösungswege anzustreben und nicht alles über Informationstechnologie lösen zu wollen. Größtenteils wird der Smartmetermarkt von Anbietern getrieben die sich neue Einnahmequellen versprechen und die Vorteile werden in den meisten Haushalten weder gesehen - noch existieren diese). Das BNetzA Papier sieht dies aber auch (Leitgedanke 4). Was mir persönlich im Papier fehlt ist ein klarere Fokus auf das Thema Datenschutz, Schutz von kritischen Infrastrukturen und Monokultur. Die Notwendigkeit von Datendrehscheiben wird nicht hinterfragt, und eine Vermischung der Messdaten von Prosumenten oder Industrieabnehmern mit den Messstellen in Haushalten führt meiner Meinung nach zu einer Übertechnisierung der Haushalte. Für eine zuverlässige Demand-Site Prognose ist meiner Meinung nach nicht notwendig den Tagesverlauf jedes einzelnen Haushaltes zu betrachten - ganz im Gegenteil das ist eher schädlich. Der Nutzen von mehr Transparenz beim Stromverbrauch eines einzelnen Haushaltes kann auch durch eine rein lokale Informationsanwendung (deutlich besser) gelöst werden. Eine feingranulare Übermittlung von Messwerten erscheint mir nicht zwingend notwendig und vor allem nicht Ökonomisch. In dem Zusammenhang verweise ich auch auf den Artikel in der aktuellen Datenschleuder #95 (Power to the People, Das Stromnetz der Zukunft, Mathias Dalheimer, Seite 35-48) und die Projekte mySmartGrid sowie volkszaehler.org.

Facebook Events im Google Kalender

Wer ein Smartphone mit sich rumschleppt wird schon auf die Idee gekommen sein einen Web Kalender wie z.B. Google Calendar zu synchronisieren, um immer alle Termine im Blick zu haben. In meinem Fall habe ich einen Google Calendar Account in dem ich einige andere Kalender zusammenfasse, dieser wird dann per CalDAV auf dem iPhone eingebunden. So sehe ich zum Beispiel die Karlsruher IT Termine, die Familientermine und die Termine der Karlsruher Piraten.

Screenshot Facebook Event ExportWas mir bisher noch gefehlt hat, waren meine Facebook Events, da hier immer auch mal wieder eine Einladung dabei ist, der ich zwar bei Facebook zusage, diese dann aber nicht in einen der Google Calender übernehme. Es gibt hier aber eine einfache Möglichkeit, man kann die Events in Facebook als webcal/ical Feed exportieren. Diese URL kann man dann in Google als neuen Kalender von einer URL importieren.

Das hat aber leider das Problem, dass Google bei einigen Einträgen kein Titel oder Beschreibung anzeigt. Das ist ein bekanntes Problem, und es gibt im Web auch Anleitungen wie man das beheben kann. Bei mir hat das auch geklappt (allerdings musste ich nicht CLASS:CONFIDENTIAL in CLASS:PUBLIC ändern, sondern bei mir waren es CLASS:PRIVATE Einträge die Facebook produziert hat: Diese Lösung setzt allerdings voraus, dass man irgendwo ein PHP Script ablegen kann. Damit muss man jetzt nur die Export-URL von Facebook in dem PHP Script hinterlegen ("webcal:" in "http:" ändern), und in Google die PHP URL als Kalender importieren. Dabei ist zu beachten, dass jeder der das Script kennt und aufrufen kann so an alle Facebook Termine rankommt (auch die privaten).

Wer das mit dem eigenen PHP Script nicht machen will, der kann auch Yahoo! Pipes verwenden. Das ist ein Dienst bei den man eine Verarbeitungspipeline für Feeds zusammenstellen kann. Im Falle von ICS Dateien reicht es bei Yahoo aus diese nur als Source zu definieren, und dann direkt wieder zurückzugeben, denn die Yahoo Quelle für Feeds (die ICS versteht) filtert automatisch die CLASS Attribute ganz raus.

Bei Yahoo Pipes muss man immer etwas tricksen wenn man einzelne Komponenten verbinden möchte (auf den Typ achten), deswegen hat die von mir verwendete Pipe noch den Zwischenschritt mit dem URL-Builder. Die URL selbst ist in einem "Private String" abgelegt, damit niemand der die Sourcen sehen kann an meine Facebook Events herankommt. (Leider lässt sich aber die Pipe trotzdem nicht sinnvoll Sharen, aber ich denke mit dem Screenshot könnt Ihr Euch leicht eine eigene zusammenbauen). Beim Aufruf der Pipe ist es wichtig den Parameter "_render=ical" mitzugeben. Früher gab es hierfür wohl einen Menueintrag bei Yahoo, der scheint aber entfernt worden zu sein. Auch hier gilt, wer die Addresse Eurer Pipe kennt, kann Eure Facebook Terminzusagen lesen.

E-Mail versenden von Amazon EC2

Amazon EC2 ist ein IaaS Anbieter bei dem man eigene virtuelle Systeme starten kann. Diese Systeme können dann alle Systemfunktionen die eine Anwendung benötigt bereitstellen. Dazu gehört zum Beispiel auch der E-Mail Versand (z.B. root Mails oder eben E-Mails der Anwendung wie z.B. Passwort Reminder oder Notifications). Um sicherzustellen, dass die E-Mails die von einem Amazon EC2 System abgeschickt werden auch ankommen, und nicht in Spam Filter der Provider hängenbleiben sind folgende Punkte zu beachten: a) wenn man einen Mailserver (MTA) verwendet der die Mails versendet so sollte dieser natürlich sicher konfiguriert sein, und kein Relaying von E-Mails zulassen - da sonst der EC2 Server zur Spamschleuder wird (was nicht nur Ärger mit Amazon nach sich zieht). b) Der SMTP Server meldet sich mit einem Rechnername. Dort sollte er nicht den internen Amazon EC2 Namen verwenden der dem Rechner zugewiesen wird, denn dir dort verwendete .internal Domain wird von vielen E-Mail Empfängern als ungültig abgelehnt. c) Die E-Mail Adresse des SMTP Servers (also in dem Fall der virtuelle EC2 Host) sollte in einen gültigen Hostnamen aufgelöst werden, denn sonst springt der Spamschutz der Empfänger an. Dies ist bei Amazon nur sinnvoll machbar wenn man eine Elastic IP verwendet. Dieses Verfahren wird PTR oder auch "reverse DNS" checks genannt. d) Der Envelop-From (im Falle von Cron Mails z. B. root@host) der versendeten E-Mails sollte eine gültige E-Mail Adresse sein. Insbesondere prüfen E-Mail Server beim Empfang, ob die Domain existiert. Hier sollte also auch nicht der .internal Hostname von AWS verwendet werden. e) Die Domain einer Absender E-Mail Adresse sollten nicht nur gültig sein, sondern mit dem SPF Mechanismus (Sender Policy Framework) kann der Betreiber der Domain auch angeben von welchen Rechnern legalerweise Mails mit dem Absender versendet werden. Einige Empfänger benutzen das, um das Spamaufkommen zu reduzieren. In der Regel kommt in der Liste der zugelassenen Server nur die MX Server der Domain vor, und eben nicht beliebige IP-Adressen aus den Amazon AWS Netzen. Es bietet sich auch nicht an das komplette EC2 Netz in die Liste aufzunehmen, da sonst alle Amazon Kunden wiederum E-Mails am SPF Filter vorbei versenden können (das ist zwar weniger ein Problem aber eben unschön). Aus diesen Punkten ergeben sich zwar einige Konfigurationen die man vornehmen kann um von einem EC2 Host direkt an Endempfänger Mails zu versenden. Die Wahrscheinlichkeit dass diese aber häufig als Spam erkannt werden ist groß. Deshalb ist es anzuraten, dass ein Smarthost verwendet wird. (Das hat auch administrative Vorteile wenn die EC2 Hosts Ihre Mails schnell loswerden und man nicht mehrere Mailwarteschlangen überwachen muss). Um kein eigenes Relay betreiben zu müssen bietet es sich z.B. an den Simple E-Mail Service (SES) von Amazon zu verwenden. Dann muss man nur die SES E-Mail Server von Amazon AWS zusätzlich in die SPF Liste der Absenderdomain aufnehmen. Dies ist hier beschrieben: http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/SPF.html. Im Gegensatz zum kompletten IP-Subnetz von Amazon EC2 hat man bei dieser Vorgehensweise den Vorteil, dass Amazon ein hohes Interesse daran hat, dass nur autorisierte Benutzer die Absenderadresse verwenden. (Gleiches gilt übrigens auch für Google App Engine, hier kann man auch Mail Relays in die SPF Liste aufnehmen). Zudem kann man sich danke Amazon IAM ein Benutzername/Passwort erzeugen der außer dem E-Mail Versand keine weiteren Rechte hat. Dieser kann man dann bedenkenlos im EC2 Image hinterlegen - zumindest solange man dieses nicht mit anderen Amazon Anwendern teilt. Sonst bietet es sich eher an diese Credentials beim Start mit anzugeben. Im nächsten Post beschreibe ich, wie man bei einem AMI auf Basis von Amazon Linux diese Varianten realisieren kann.

Rechner im AD LDAP mit Powershell auflisten

Ich hab ein wenig mit Powershell herumgespielt (weil ich das Script das auch in de.Hackin9.org 08/2011 abgebildet war nicht verstanden habe). Dabei kam dann folgendes funktionierendes Script heraus, es sortiert mit alle Rechner des AD LDAPs und zeigt diese in einem grafischen Viewer mit Betriebsystemversion und Servicepack Level an:
$ldapSearcher = new-object directoryservices.directorysearcher;
$ldapSearcher.filter = "(objectclass=computer)";
$computers = $ldapSearcher.findall();
$pcs = @();
foreach ($c in $computers) {
  $pc = "" | Select-Object Name,OS,SP,SPN;
  $pc.Name=$c.properties["cn"];
  $pc.OS=$c.properties["operatingsystem"];
  $pc.SP=$c.properties["operatingsystemservicepack"];
  $pc.SPN=$c.properties["serviceprincipalname"];
  $pcs += $pc;
}
$pcs | sort-object OS,SP,Name | Out-GridView;
Ich habe aber keine Ahnung wie man einfacher aus den Dictionary Entries des $c.Properties Member direkte Properties machen kann ohne diese mit einer foreach Schleife und direktem Assignment aufwändig zu kopieren. Ich hoffe ein mitlesender Powershell Guru kann mir das kurz sagen? :) Update: Max Trinidad (@MaxTrinidad) hat mich auf die Idee mit New-Object gebracht, damit lässt sich das Script etwas vereinfachen und die Attribute in Strings konvertieren:
$ldapSearcher = new-object directoryservices.directorysearcher;
$ldapSearcher.filter = "(objectclass=computer)";
$computers = $ldapSearcher.findall();
[Array] $pcs = $null;
foreach($c in $computers) {    
    $pcs += New-Object PSobject -property @{
        Name = [string]$c.properties["cn"];
        OS = [string]$c.properties["operatingsystem"];
        SP = [string]$c.properties["operatingsystemservicepack"];
        SPN = [string]$c.properties["serviceprincipalname"]; }
}
Und darauf aufbauend (aber ohne String Konvertierung) dann die Lösung mit der Automatischen Übernahme aller Dictionary Einträge aus dem AD Objekt:
$ldapSearcher = New-Object directoryservices.directorysearcher;
$ldapSearcher.filter = "(objectclass=computer)";
$computers = $ldapSearcher.findall();
[Array] $pcs = $null;
$computers | ForEach-Object { $pcs += New-Object PSobject -property $_.Properties; }
$pcs | Out-GridView;

IPv6 default address selection in Java

Da bei IPv6 Nodes (also Rechner und andere Teilnehmer an einem Netzwerk) schon konzeptionell mehrere Adressen haben ist die Auswahl welche Quell- und Zieladresse verwendet werden soll eine wichtige Funktion. Insbesondere dann wenn ein Rechner im Dual-Stack Betrieb sowohl IPv4 als auch IPv6 Adressen erreichen kann. Bei IPv6 kann jedes Netzwerkinterface mehrere IPv6 Adressen haben, die dann entweder in unterschiedlichen Bereichen (Scopes) genutzt werden können (localhost, link, site, global) oder die unterschiedliche Bevorzugt (valid, prefered) sind. Durch die Unterstützung von Renumbering (stateless autoconfiguration) haben Adressen unterschiedliche Lebenszeiten. Zudem gibt es Adressen die über eine Migrations-/Tunnel Technologie wie Toredo, ISATAP oder 6to4 bereitgestellt werden, und nicht immer benutzt werden sollen. Idealerweise würde eine Anwendung oder das Betriebssystem alle möglichen Quell/Ziel-Adresskombinationen ermitteln, und alle (aussichtsreichsten zuerst) durchprobieren. RFC 3484 beschreibt ein Verfahren für die Default Address Selection für IPv6. Der von Microsoft Research verfasste Entwurf gibt Regeln vor wie die Auswahl von Ziel- und Quelladressen zu geschehen hat, und definiert auch eine Möglichkeit dass der Administrator eines Systems eigene Gewichtungen definieren kann. Ideal wäre eine Laufzeit Funktion, der man einen Hostnamen übergibt, und die dann die Verbindung zur Gegenstelle herstellt und dabei alle Regeln des RFC 3484 (und dringend notwendiger zukünftiger Verbesserungen) beachtet. Durch die Trennung zwischen Kernel und Usermode, und aus Gründen der Kompatibilität mit existierendem Netzwerkcode verwenden die meisten* Systeme allerdings ein anderes Verfahren. Bestehende Funktionen wie z.B. getaddrinfo(3) wurden erweitert: die Auflösung von Hostnamen in Adressen liefert jetzt eine nach Präferenzen sortierte Liste der Zieladressen zurück. Dabei greift die Bibliotheksfunktion auf Adress- und Routinginformationen des Kernels zurück. Denn es müssen für jede zurückgelieferte Zieladresse auch die potentiellen Quelladressen bestimmt und bewertet werden. Unter Windows kann die Sortierung mit der prefixpolicy (netsh.exe interface ipv6 show prefixpolicies) angezeigt werden. Linux Systeme speichern die Konfiguration in /etc/gai.conf, aktuelle Einstellungen können mit dem iproute2 Paket angesehen werden (ip addrlabel). Das ganze ist im Kernel und der glibc implementiert. Auch bei Java wurde kein komplett neues Verfahren für den Verbindungsaufbau für IPv6 definiert**. Die Anwendung selbst ist dafür zuständig alle möglichen Zieladressen der Reihe nach durchzuprobieren. Wenn die Anwendung keine Quelladresse angibt (was sie vermeiden sollte) so wird dann der Kernel für jeden der Zieladressen eine Quelladresse auswählen. Wenn eine Adresse nicht erreichbar ist, so muss die nächste Adresse verwendet werden. Wenn alle Adressen nicht erreichbar sind, so sollte eine Fehlermeldung zurückgegeben werden die alle probierten Zieladressen benennt und den ersten (oder alle) Fehlermeldungen benennt. Beispielhaft kann dies so aussehen:
Socket connectToHost(String host, int port)
	throws UnknownHostException, SocketException
{
	IOException firstException = null;

	InetAddress[] addressArray = InetAddress.getAllByName(host);
	for(InetAddress addr : addressArray)
	{
		try {
			return new Socket(addr, port);
		} catch (IOException ex) {
			if (firstException == null)
				firstException = ex;
		}
	}

        // build informative error message
	StringBuilder msg = new StringBuilder("Unable to connect to host=");
	msg.append(host); msg.append(" port="); 
        msg.append(String.valueOf(port)); msg.append(" [");
	
	for(int i=0;i < addressArray.length;i++)
	{
		if (i != 0)
			msg.append(',');
		msg.append(addressArray[i]);
	}
	msg.append("]: "); msg.append(firstException.getMessage());
	SocketException se = new SocketException(msg.toString());
	se.initCause(firstException);
	throw se;
}
Dieser Code überlässt die Auswahl einer Quelle dem Kernel (es werden also nicht alle möglichen Kombinationen durchprobiert). Ebenso ist kein Handling für Timeouts enthalten, und ein Cache der Verbindungszustände erinnert oder gar ein paralleler Aufbau zu mehreren Zielen ist noch nicht enthalten. Trotzdem ist der Code schon recht komplex, sollte also nicht mehrfach implementiert werden müssen. Von InetAddress.getByName(String host) würde ich auf jeden Fall Abstand nehmen. Diese Methode gibt nur die bevorzugte Addresse zurück, und führt bei DualStack Anwendungen dazu, dass nicht IPv6 und IPv4 Adressen durchprobiert werden. * Microsoft ist typischerweise Entwicklerfreundlicher und muss weniger Rücksicht nehmen auf etablierte APIs, deswegen gibt es die Funktion WSAConnectByName() die alle Addressen selbst durchprobiert. ** Java kennt Socket(String name, int port), dieser Konstruktor verwendet aber keine Schleife um alle möglichen Adressen zu kontaktieren.

Windows 7 hosts-file ignoriert IP Addressen mit 0 prefix

Mir ist gerade zufällig aufgefallen (als ich diesen Bugreport nachgestellt habe) dass Windows 7 IPv4 Address-Einträge die eine führende 0 haben im hosts file ignoriert:
C:\Windows\system32>echo 9.20.187.06 TestHost
  >> %SystemRoot%\system32\Drivers\etc\hosts

C:\Windows\system32>ipconfig /displaydns | find "A-Eintrag"
    AAAA-Eintrag  . . . . : 2001::1
    AAAA-Eintrag  . . . . : fe80::20d:60ff:fe49:47
    AAAA-Eintrag  . . . . : 2001::2
    (Host-)A-Eintrag  . . : 9.20.187.96

C:\Windows\system32>echo 9.20.187.6 TestHost
  >> %SystemRoot%\system32\Drivers\etc\hosts

C:\Windows\system32>ipconfig /displaydns | find "A-Eintrag"
    AAAA-Eintrag  . . . . : 2001::1
    AAAA-Eintrag  . . . . : fe80::20d:60ff:fe49:47
    AAAA-Eintrag  . . . . : 2001::2
    (Host-)A-Eintrag  . . : 9.20.187.96
    (Host-)A-Eintrag  . . : 9.20.187.6