Category: administration

Hardware-Zufallszahlengenerator des Raspberry Pi nutzen

29.10.2015 yahe administration legacy linux security

Bei Serversystemen hat man häufig das Problem, dass dem Zufallszahlengenerator nicht genug Nutzerinteraktionen zur Verfügung stehen, um ausreichend Entropie für den Zufallszahlengenerator zu sammeln. Das führt dann, je nach eingesetzter Software, zu hängenden SSH-Verbindungen, zu Timeouts bei HTTPS-Verbindungen oder anderen nicht reproduzierbaren Fehlern. Aus diesem und anderen Gründen haben CPU-Hersteller begonnen, Hardware-Zufallszahlengeneratoren direkt in ihre Prozessoren zu integrieren. Bei Intel nennt sich diese Technologie "Secure Key" (vormals "Bull Mountain"), bei VIA ist sie als "PadLock" bekannt, einige Broadcom-SOCs haben einen Hardware-Zufallszahlengenerator und auch AMD Zen unterstützt RdRand.

Auch der Broadcom-Chip des Raspberry Pi bietet einen Hardware-Zufallszahlengenerator, dessen erzeugte Zufallszahlen man in den Linux-Zufallszahlenpool einfließen lassen kann. Hierfür sind nur wenige Schritte notwendig. Als erstes muss das entsprechende Kernel-Modul geladen werden. Die Distribution Raspbian liefert zwei Broadcom-Random-Number-Generator-Module mit den Namen "bcm2708_rng" und "bcm2835_rng". Obwohl im Raspberry Pi ein Broadcom 2835 steckt, funktioniert in Raspbian nur das "bcm2708_rng"-Kernelmodul korrekt. BCM2708 ist die SOC-Familie, zu der auch der BCM2835 gehört. Überprüfen kann man das auch nochmal anhand der Ausgabe des Befehls:

cat /proc/cpuinfo

In der erscheinenden Ausgabe wird die Hardware explizit als BCM2708 benannt:

processor  : 0
model name  : ARMv6-compatible processor rev 7 (v6l)
BogoMIPS  : 2.00
Features  : half thumb fastmult vfp edsp java tls
CPU implementer  : 0x41
CPU architecture: 7
CPU variant  : 0x0
CPU part  : 0xb76
CPU revision  : 7

Hardware  : BCM2708
Revision  : 000e
Serial    : ****************

Nun heißt es, das passende Kernelmodul zu laden. Testweise könnt ihr das mit folgendem Befehl tun:

sudo modprobe bcm2708_rng

Wenn das ohne Fehler funktioniert hat, sollte nun das Character-Device-File "/dev/hwrng" existieren. Ist das der Fall, benötigt ihr noch die "rng-tools", die ihr einfach per "apt-get" installieren könnt:

sudo apt-get install rng-tools

Nach der Installation sollten diese direkt automatisch starten. Ihr könnt zudem testen, ob diese auch ihre Arbeit verrichten. Dazu ruft ihr das "rngd" Tool im Foreground-Modus auf und beendet es nach ein wenig Warten wieder per "CTRL+C":

sudo rngd -f
^C

Euch sollten nun die verschiedensten Statistiken angezeigt werden, unter anderem, wieviele FIPS 140-2 Tests erfolgreich bestanden wurden. Mit diesen wird die Qualität der generierten Zufallszahlen ermittelt:

rngd 2-unofficial-mt.14 starting up...
entropy feed to the kernel ready
stats: bits received from HRNG source: 60064
stats: bits sent to kernel pool: 512
stats: entropy added to kernel pool: 512
stats: FIPS 140-2 successes: 3
stats: FIPS 140-2 failures: 0
stats: FIPS 140-2(2001-10-10) Monobit: 0
stats: FIPS 140-2(2001-10-10) Poker: 0
stats: FIPS 140-2(2001-10-10) Runs: 0
stats: FIPS 140-2(2001-10-10) Long run: 0
stats: FIPS 140-2(2001-10-10) Continuous run: 0
stats: HRNG source speed: (min=752.012; avg=782.220; max=807.043)Kibits/s
stats: FIPS tests speed: (min=5.249; avg=5.533; max=6.076)Mibits/s
stats: Lowest ready-buffers level: 2
stats: Entropy starvations: 0
stats: Time spent starving for entropy: (min=0; avg=0.000; max=0)us
Exiting...

Wenn das alles funktioniert hat, könnt ihr die Verwendung des Kernel-Moduls permanent machen, indem ihr in die Datei "/etc/modules" eine neue Zeile mit dem Namen des Kernel-Moduls einfügt:

bcm2708_rng

Um sicherzugehen, dass die bereitgestellten Zufallszahlen wirklich eine gute Qualität haben, habe ich den vollständigen Satz an Dieharder-Tests laufen lassen. Hierzu habe ich erst einmal Dieharder selbst installiert:

sudo apt-get install dieharder

Da der Zufallszahlengenerator nicht sonderlich schnell ist, musste ich den Dieharder-Test via "nohup" starten. Dieses sorgt dafür, dass der Prozess weiterläuft, obwohl die SSH-Session, in der der Aufruf erfolgte, bereits geschlossen wurde. Der gesamte Dieharder-Testlauf dauerte etwa einen Monat:

sudo nohup sh -c "cat /dev/hwrng | dieharder -g 200 -a"

Die Ergebnisse der Dieharder-Tests sind aus meiner Sicht vielversprechend. Fast alle Testläufe wurden ohne Probleme bestanden. Lediglich zwei Testläufe zeigen ein "weak"-Ergebnis, was bei einem echten Zufallszahlengenerator durchaus mal vorkommen kann:

#=============================================================================#
#            dieharder version 3.31.1 Copyright 2003 Robert G. Brown          #
#=============================================================================#
   rng_name    |rands/second|   Seed   |
stdin_input_raw|  2.49e+04  |1848243654|
#=============================================================================#
        test_name   |ntup| tsamples |psamples|  p-value |Assessment
#=============================================================================#
   diehard_birthdays|   0|       100|     100|0.80754184|  PASSED
      diehard_operm5|   0|   1000000|     100|0.46154223|  PASSED
  diehard_rank_32x32|   0|     40000|     100|0.24741511|  PASSED
    diehard_rank_6x8|   0|    100000|     100|0.98544914|  PASSED
   diehard_bitstream|   0|   2097152|     100|0.74214657|  PASSED
        diehard_opso|   0|   2097152|     100|0.62926414|  PASSED
        diehard_oqso|   0|   2097152|     100|0.65459762|  PASSED
         diehard_dna|   0|   2097152|     100|0.05329859|  PASSED
diehard_count_1s_str|   0|    256000|     100|0.93984253|  PASSED
diehard_count_1s_byt|   0|    256000|     100|0.73264960|  PASSED
 diehard_parking_lot|   0|     12000|     100|0.82688871|  PASSED
    diehard_2dsphere|   2|      8000|     100|0.81340970|  PASSED
    diehard_3dsphere|   3|      4000|     100|0.81943245|  PASSED
     diehard_squeeze|   0|    100000|     100|0.20437793|  PASSED
        diehard_sums|   0|       100|     100|0.01259559|  PASSED
        diehard_runs|   0|    100000|     100|0.40514608|  PASSED
        diehard_runs|   0|    100000|     100|0.83339986|  PASSED
       diehard_craps|   0|    200000|     100|0.11679660|  PASSED
       diehard_craps|   0|    200000|     100|0.42675629|  PASSED
 marsaglia_tsang_gcd|   0|  10000000|     100|0.96356183|  PASSED
 marsaglia_tsang_gcd|   0|  10000000|     100|0.84443650|  PASSED
         sts_monobit|   1|    100000|     100|0.86875820|  PASSED
            sts_runs|   2|    100000|     100|0.76068017|  PASSED
          sts_serial|   1|    100000|     100|0.44501629|  PASSED
          sts_serial|   2|    100000|     100|0.08213384|  PASSED
          sts_serial|   3|    100000|     100|0.40056497|  PASSED
          sts_serial|   3|    100000|     100|0.84781124|  PASSED
          sts_serial|   4|    100000|     100|0.67540322|  PASSED
          sts_serial|   4|    100000|     100|0.67479165|  PASSED
          sts_serial|   5|    100000|     100|0.93910920|  PASSED
          sts_serial|   5|    100000|     100|0.78108786|  PASSED
          sts_serial|   6|    100000|     100|0.36072231|  PASSED
          sts_serial|   6|    100000|     100|0.10943079|  PASSED
          sts_serial|   7|    100000|     100|0.33454672|  PASSED
          sts_serial|   7|    100000|     100|0.84662732|  PASSED
          sts_serial|   8|    100000|     100|0.56239211|  PASSED
          sts_serial|   8|    100000|     100|0.85989154|  PASSED
          sts_serial|   9|    100000|     100|0.09053360|  PASSED
          sts_serial|   9|    100000|     100|0.76921152|  PASSED
          sts_serial|  10|    100000|     100|0.65164012|  PASSED
          sts_serial|  10|    100000|     100|0.98802936|  PASSED
          sts_serial|  11|    100000|     100|0.16490199|  PASSED
          sts_serial|  11|    100000|     100|0.43350451|  PASSED
          sts_serial|  12|    100000|     100|0.97347736|  PASSED
          sts_serial|  12|    100000|     100|0.35755776|  PASSED
          sts_serial|  13|    100000|     100|0.61727927|  PASSED
          sts_serial|  13|    100000|     100|0.70426071|  PASSED
          sts_serial|  14|    100000|     100|0.96653473|  PASSED
          sts_serial|  14|    100000|     100|0.72003802|  PASSED
          sts_serial|  15|    100000|     100|0.33951664|  PASSED
          sts_serial|  15|    100000|     100|0.65324653|  PASSED
          sts_serial|  16|    100000|     100|0.79539647|  PASSED
          sts_serial|  16|    100000|     100|0.29465210|  PASSED
         rgb_bitdist|   1|    100000|     100|0.29377788|  PASSED
         rgb_bitdist|   2|    100000|     100|0.26581318|  PASSED
         rgb_bitdist|   3|    100000|     100|0.68619130|  PASSED
         rgb_bitdist|   4|    100000|     100|0.65469598|  PASSED
         rgb_bitdist|   5|    100000|     100|0.94194728|  PASSED
         rgb_bitdist|   6|    100000|     100|0.06186682|  PASSED
         rgb_bitdist|   7|    100000|     100|0.99989483|   WEAK
         rgb_bitdist|   8|    100000|     100|0.94851172|  PASSED
         rgb_bitdist|   9|    100000|     100|0.55403191|  PASSED
         rgb_bitdist|  10|    100000|     100|0.42372957|  PASSED
         rgb_bitdist|  11|    100000|     100|0.69930031|  PASSED
         rgb_bitdist|  12|    100000|     100|0.10435458|  PASSED
rgb_minimum_distance|   2|     10000|    1000|0.06686667|  PASSED
rgb_minimum_distance|   3|     10000|    1000|0.73245892|  PASSED
rgb_minimum_distance|   4|     10000|    1000|0.21902555|  PASSED
rgb_minimum_distance|   5|     10000|    1000|0.98142677|  PASSED
    rgb_permutations|   2|    100000|     100|0.52202354|  PASSED
    rgb_permutations|   3|    100000|     100|0.01765637|  PASSED
    rgb_permutations|   4|    100000|     100|0.54444953|  PASSED
    rgb_permutations|   5|    100000|     100|0.98885119|  PASSED
      rgb_lagged_sum|   0|   1000000|     100|0.19012074|  PASSED
      rgb_lagged_sum|   1|   1000000|     100|0.52956823|  PASSED
      rgb_lagged_sum|   2|   1000000|     100|0.95958846|  PASSED
      rgb_lagged_sum|   3|   1000000|     100|0.95145243|  PASSED
      rgb_lagged_sum|   4|   1000000|     100|0.13284355|  PASSED
      rgb_lagged_sum|   5|   1000000|     100|0.50651321|  PASSED
      rgb_lagged_sum|   6|   1000000|     100|0.67578443|  PASSED
      rgb_lagged_sum|   7|   1000000|     100|0.28159075|  PASSED
      rgb_lagged_sum|   8|   1000000|     100|0.70439598|  PASSED
      rgb_lagged_sum|   9|   1000000|     100|0.95701984|  PASSED
      rgb_lagged_sum|  10|   1000000|     100|0.11591970|  PASSED
      rgb_lagged_sum|  11|   1000000|     100|0.79622842|  PASSED
      rgb_lagged_sum|  12|   1000000|     100|0.30363114|  PASSED
      rgb_lagged_sum|  13|   1000000|     100|0.66673746|  PASSED
      rgb_lagged_sum|  14|   1000000|     100|0.08939797|  PASSED
      rgb_lagged_sum|  15|   1000000|     100|0.15096120|  PASSED
      rgb_lagged_sum|  16|   1000000|     100|0.31977071|  PASSED
      rgb_lagged_sum|  17|   1000000|     100|0.27180216|  PASSED
      rgb_lagged_sum|  18|   1000000|     100|0.69988188|  PASSED
      rgb_lagged_sum|  19|   1000000|     100|0.47622405|  PASSED
      rgb_lagged_sum|  20|   1000000|     100|0.69420826|  PASSED
      rgb_lagged_sum|  21|   1000000|     100|0.99297760|  PASSED
      rgb_lagged_sum|  22|   1000000|     100|0.60131473|  PASSED
      rgb_lagged_sum|  23|   1000000|     100|0.62287604|  PASSED
      rgb_lagged_sum|  24|   1000000|     100|0.70013973|  PASSED
      rgb_lagged_sum|  25|   1000000|     100|0.65860222|  PASSED
      rgb_lagged_sum|  26|   1000000|     100|0.93448843|  PASSED
      rgb_lagged_sum|  27|   1000000|     100|0.99635422|   WEAK
      rgb_lagged_sum|  28|   1000000|     100|0.29584189|  PASSED
      rgb_lagged_sum|  29|   1000000|     100|0.81313054|  PASSED
      rgb_lagged_sum|  30|   1000000|     100|0.64124408|  PASSED
      rgb_lagged_sum|  31|   1000000|     100|0.94116582|  PASSED
      rgb_lagged_sum|  32|   1000000|     100|0.96364896|  PASSED
     rgb_kstest_test|   0|     10000|    1000|0.13971491|  PASSED
     dab_bytedistrib|   0|  51200000|       1|0.65378962|  PASSED
             dab_dct| 256|     50000|       1|0.86099385|  PASSED
Preparing to run test 207.  ntuple = 0
        dab_filltree|  32|  15000000|       1|0.80254877|  PASSED
        dab_filltree|  32|  15000000|       1|0.56933154|  PASSED
Preparing to run test 208.  ntuple = 0
       dab_filltree2|   0|   5000000|       1|0.71661701|  PASSED
       dab_filltree2|   1|   5000000|       1|0.34205581|  PASSED
Preparing to run test 209.  ntuple = 0
        dab_monobit2|  12|  65000000|       1|0.80589488|  PASSED

IPFire via Mac OS X auf ALIX 2D13 installieren

01.10.2015 yahe administration legacy linux security

Lange Zeit habe ich die kleine Firewall-Distribution m0n0wall eingesetzt, um die Edge-Firewall, sprich, die äußerste Firewall zwischen dem eigenen Netz und dem Internet, in meinem heimischen Netzwerk zu betreiben. Leider wurde die Weiterentwicklung von m0n0wall vor einer Weile eingestellt. Ich musste also früher oder später einen Ersatz finden. Ich sah mich ein wenig um und fand das deutsche Projekt IPFire, einen Fork des sehr bekannten, aber nicht mehr gepflegten IPCop.

Als Hardware sollte mein bereits vorhandenes ALIX 2D13 von PC Engines zum Einsatz kommen. Dabei handelt es sich um ein Embedded-Board mit CF-Kartenleser, serieller Schnittstelle und einer 500MHz AMD Geode LX800 CPU. Von seinem Bruder, dem ALIX 2D3, unterscheidet es sich eigentlich nur durch die Pufferbatterie für die BIOS-Uhrzeit.

Um mit der seriellen Schnittstelle kommunizieren zu können, braucht man einen USB-zu-Seriell-Adapter, den man problemlos bei Amazon finden kann. Diese enthalten typischerweise den Prolific PL2303 Chipsatz, für den man die passenden Treiber beim Hersteller finden kann. Vergesst nicht das Nullmodem-Kabel, das ihr ebenfalls brauchen werdet. Auch das findet man bei Amazon. Um die CF-Karte beschreiben zu können, braucht man weiterhin einen CF-Kartenleser, den man ebenfalls bei Amazon findet. Abschließend braucht man noch eine ordentliche Terminalemulation. Hier habe ich dieses Mal ZOC ausprobiert, das man 30 Tage lang kostenlos testen kann.

Bevor wir mit dem Installieren von IPFire anfangen können, müssen wir wahrscheinlich erst einmal die Firmware des ALIX-Boards aktualisieren. Alle wichtigen Informationen zum ALIX-Board findet man auf der dazugehörigen Support-Seite, inklusive der Firmware-Version 0.99m. Mit dieser allein können wir jedoch noch nichts anfangen. Wir benötigen noch ein Betriebssystem, das wir auf dem ALIX-Board booten können, um das Firmware-Image flashen zu können. Auf der Webseite von PC Engines sind mehrere Möglichkeiten beschrieben, wie man das mit einem FreeDOS-Image lösen kann. Die aus meiner Sicht einfachste Variante ist es, das fertige Image herunterladen, dieses auf eine CF-Karte flashen, die Dateien aus der ebenfalls heruntergeladenen Firmware-Version 0.99m mit auf die CF-Karte kopieren und das ganze Paket anschließend booten und verwenden:

  1. Wenn wir das FreeDOS-Image entpackt haben, haben wir eine Datei namens "./freedos_alixupdate_0.99.img".
  2. Um die Datei zu flashen, stecken wir die CF-Karte in den CF-Kartenleser.
  3. Wir öffnen die Terminal-Anwendung von macOS und wechseln in den Ordner, in dem das FreeDOS-Image liegt.
  4. Nun lassen wir uns mit dem Befehl "diskutil list" die Liste der gemounteten Speichermedien anzeigen.
  5. In dieser Liste müssen wir den Namen der CF-Karte ausfindig machen, der die Form "/dev/disk<Zahl>" haben sollte. Man kann sich dabei z.B. an der Größe der CF-Karte orientieren.
  6. Wenn wir den Namen der CF-Karte ausfindig gemacht haben, können wir die entsprechende Disk mit dem Befehl "diskutil unmountDisk /dev/disk<Zahl>" unmounten.
  7. Nun sind wir soweit, dass wir das FreeDOS-Image mit dem Befehl "sudo dd if=./freedos_alixupdate_0.99.img of=/dev/disk<Zahl>" auf die CF-Karte flashen können.
  8. Das Flashen sollte relativ schnell gehen. Danach sollte macOS die Karte wieder erkennen und euch ermöglichen, die Dateien darauf zu bearbeiten.
  9. Nun könnt ihr die Dateien aus dem entpackten ALIX-Firmware-Download auf die CF-Karte kopieren ("./alix1.bin", "./ALIX1.IMG", "./alix2.bin", "./ALIX2.IMG", "./alix5.bin", "./ALIX5.IMG", "./alixbios.txt" und "./sb.com").
  10. Abschließend könnt ihr die CF-Karte mit dem Befehl "diskutil eject /dev/disk<Zahl>" auswerfen.

Die so vorbereitete Karte könnt ihr nun verwenden, um die Firmware eures ALIX-Boards zu aktualisieren. Das geht leider nicht vollkommen automatisch. Deshalb müssen wir nun alles vorbereiten, um die serielle Konsole des ALIX-Boards verwenden zu können. Hierfür solltet ihr als erstes mal die Treiber eures USB-zu-Seriell-Adapters installieren. Bei mir musste das Systems dabei neu gestartet werden. Nach der Installation sollten zwei neue Devices im System erscheinen ("/dev/cu.usbserial" und "/dev/tty.usbserial"). Wenn das der Fall ist, könnt ihr nun euren Terminalemulator starten. Hier müsst ihr nun konfigurieren, dass sich der Emulator mit der seriellen Schnittstelle des ALIX-Boards verbinden kann. Hier Schritt für Schritt das Vorgehen für ZOC:

  1. Verbindet den USB-Stecker eures USB-zu-Seriell-Adapters mit eurem Mac.
  2. Öffnet ZOC und startet eine neue Verbindung.
  3. Im sich neu öffnenden Einstellungsfenster gebt ihr ein, dass ihr das Verbindungsprofil "Standard.zoc" verwenden wollt, als Verbindungstyp setzt ihr "Serial/Direct" und als Emulation nehmt ihr "Xterm".
  4. Nun klickt ihr auf den "Bearbeiten..."-Button neben dem Verbindungsprofil, wodurch sich ein weiteres Fenster öffnet.
  5. Im neuen Fenster geht ihr links in der Liste auf "Verbindungstyp" und wählt "Serial/Direct" aus. Beim COM-Port drückt ihr nun auf "Scan..." und solltet einen Eintrag in der Form "/dev/cu.usbserial" angezeigt bekommen, den ihr auswählt. Weiterhin wählt ihr als Baud-Geschwindigkeit den Wert "38400". Das ist der Standardwert für ALIX-2-Boards gemäß des Handbuchs.
  6. Wenn ihr das soweit habt, könnt ihr nun auf "Speichern" klicken und im vorherigen Dialog auf "Verbinden" gehen. Euch sollte nun eine Fehlermeldung angezeigt werden, die ihr einfach mit "Abbruch" wegklicken könnt.

ZOC Serial Konfiguration ZOC Serial Konfiguration ZOC Serial Konfiguration

Euch sollte nun angezeigt werden, dass die Verbindung aufgebaut werden konnte. Daher könnt ihr nun das ALIX-Board vorbereiten. Steckt die vorbereitete CF-Karte in den dafür vorgesehenen Schacht, verbindet das Nullmodem-Kabel mit dem ALIX-Board und mit dem USB-zu-Seriell-Adapter, der bereits mit eurem Mac verbunden ist. Verbindet nun abschließend das ALIX-Board mit seinem Netzteil. Das ALIX-Board sollte nun eine Speicherprüfung machen und anschließend FreeDOS booten. Das sieht im Terminalemulator in etwa so aus:

PC Engines ALIX.2 v0.99h
640 KB Base Memory
261120 KB Extended Memory

01F0 Master 848A TS4GCF133
Phys C/H/S 7769/16/63 Log C/H/S 971/128/63
FreeDOS kernel build 2036 cvs [version Aug 18 2006 compiled Aug 18 2006]...................123
Kernel compatibility 7.10 - WATCOMC - 80386 CPU required - FAT32 support

(C) Copyright 1995-2006 Pasquale J. Villani and The FreeDOS Project.
All Rights Reserved. This is free software and comes with ABSOLUTELY NO
WARRANTY; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation;
either version 2, or (at your option) any later version.
 - InitDiskWARNING: using suspect partition Pri:1 FS 06: with calculated values    3-113-49 instead of    1-242-49
C: HD1, Pri[ 1], CHS=    0-1-1, start=     0 MB, size=    15 MB
FreeDOS HIMEM64 3.26 [Aug 25 2006] (c) 1995, Till Gerken 2001-2006 tom ehlert
HIMEM - Always On A20 method used
Kernel: allocated 43 Diskbuffers = 22876 Bytes in HMA

FreeCom version 0.84-pre2 XMS_Swap [Aug 28 2006 00:29:00]
C:\>

Startet nun das Programm "sb.com" aus, das sich um das Firmware-Update kümmert:

C:\>sb.com
ALIX flash update (C)2007-2011 PC Engines GmbH
Flash ID = 2 FFFF GPI = FF
Flash ID = 1 FFFF GPI = FF
Flash ID = 0 9D37 GPI = 01
Reading 512KB flash image alix1.bin................
Compare Erase Program Verify - update OK.
C:\>

Weiterhin könnt ihr nun schonmal die Baud-Rate des ALIX-Boards neu setzen, damit ihr später IPFire problemlos über die serielle Konsole verwenden könnt. Dazu rebootet ihr das Board (z.B. indem ihr es kurz stromlos schaltet). Während beim Hochfahren noch der Speichertest des ALIX-Boards läuft, drückt ihr schnell die "S"-Taste, woraufhin das ALIX-Board zur BIOS-Konfiguration wechseln sollte, was in etwa so aussieht:

PC Engines ALIX.2 v0.99h
640 KB Base Memory
261120 KB Extended Memory

01F0 Master 848A TS4GCF133
Phys C/H/S 7769/16/63 Log C/H/S 971/128/63

BIOS setup:

(9) 9600 baud (2) 19200 baud *3* 38400 baud (5) 57600 baud (1) 115200 baud
*C* CHS mode (L) LBA mode (W) HDD wait (V) HDD slave (U) UDMA enable
(E) PXE boot enable
*M* Memory test
(P) late PCI init
*R* Serial console enable
(X) Xmodem upload
(Q) Quit

Hier gebt ihr nun die "1"-Taste ein, denn IPFire wird später für die serielle Ausgabe die Geschwindigkeit von 115200 Baud verwenden. So könnt ihr dann den Bootvorgang des ALIX-Boards und die Ausgabe von IPFire ohne Probleme verfolgen. Nach Eingabe der Taste wechselt das ALIX-Board in eine andere Ansicht, in der ihr mit der "Q"-Taste und anschließendem Druck auf die "Y"-Taste die Änderung bestätigen müsst:

(9) 9600 baud (2) 19200 baud (3) 38400 baud (5) 57600 baud *1* 115200 baud
*C* CHS mode (L) LBA mode (W) HDD wait (V) HDD slave (U) UDMA enable
(E) PXE boot enable
*M* Memory test
(P) late PCI init
*R* Serial console enable
(X) Xmodem upload
(Q) Quit

Save changes Y/N ?
Writing setup to flash... OK

Wenn ihr das geschafft habt, könnt ihr das ALIX-Board erst einmal wieder vom Strom trennen und die CF-Karte entfernen. Auf diese werden wir als nächstes das IPFire-Image installieren, das man von deren Webseite herunterladen kann. Die CF-Karte, die für IPFire verwendet wird, muss mindestens 1GB groß sein. Größere Karten sind in Ordnung. IPFire wird bei der Installation dafür sorgen, dass der größere Speicherplatz mit verwendet wird. Beim Flashen des IPFire-Images gehen wir im Grunde genauso vor, wie auch vorhin schon:

  1. Wenn wir das IPFire-Image entpackt haben, haben wir eine Datei namens "./ipfire-2.21.2gb-ext4.i586-full-core124.img".
  2. Um die Datei zu flashen, stecken wir die CF-Karte in den CF-Kartenleser.
  3. Wir öffnen die Terminal-Anwendung von macOS und wechseln in den Ordner, in dem das IPFire-Image liegt.
  4. Nun lassen wir uns mit dem Befehl "diskutil list" die Liste der gemounteten Speichermedien anzeigen.
  5. In dieser Liste müssen wir den Namen der CF-Karte ausfindig machen, der die Form "/dev/disk<Zahl>" haben sollte. Man kann sich dabei z.B. an der Größe der CF-Karte orientieren.
  6. Wenn wir den Namen der CF-Karte ausfindig gemacht haben, können wir die entsprechende Disk mit dem Befehl "diskutil unmountDisk /dev/disk<Zahl>" unmounten.
  7. Nun sind wir soweit, dass wir das IPFire-Image mit dem Befehl "sudo dd if=./ipfire-2.21.2gb-ext4.i586-full-core124.img of=/dev/disk<Zahl>" auf die CF-Karte flashen können.
  8. Das Flashen sollte dieses Mal etwas mehr Zeit in Anspruch nehmen, da es mit 1GB doch recht groß ist, habt also Geduld. macOS wird den Inhalt der CF-Karte nach dem Flash-Vorgang wahrscheinlich nicht erkennen, aber das ist okay.
  9. Wir werfen die CF-Karte einfach mit dem Befehl "diskutil eject /dev/disk<Zahl>" aus.

Bevor wir weitermachen können, müssen wir noch den Terminalemulator umkonfigurieren, damit er die Geschwindigkeit von 115200 Baud verwendet. Bei ZOC gehen wir wieder so vor, dass wir das Programm starten, eine neue Verbindung aufbauen und im Einstellungsdialog neben dem Verbindungsprofil auf "Bearbeiten..." klicken. Im neuen Dialog gehen wir wieder auf "Verbindungstyp", danach auf "Serial/Direct" und wählen die Geschwindigkeit 115200 Baud aus. Abschließend klicken wir auf "Speichern" und klönnen nunim vorherigen Dialog auf "Verbinden" gehen.

ZOC Serial Konfiguration

Nun können wir die neu geflashte CF-Karte wieder in den CF-Kartenslot des ALIX-Boards schieben und dieses mit dem Strom verbinden. Nach dem obligatorischen Speichercheck sollte nun der Bootloader GRUB geladen werden, in dem IPFire standardmäßig nach wenigen Sekunden gebootet wird. Beim ersten Hochfahren reformatiert IPFire die CF-Karte automatisch und startet dann direkt wieder neu. Beim zweiten Hochfahren sollte dann eine pseudografische Oberfläche gestartet werden, die ihr mit der Tastatur steuern könnt. Dort werden Dinge wie das Root-Passwort der seriellen Konsole und des SSH-Zugangs, das Passwort des "admin"-Accounts der Weboberfläche, sowie der Netzwerktyp und die zugehörigen Netzwerkadressen erfragt. Das initiale Setup ist im Wiki von IPFire noch ausführlicher beschrieben.

Wenn ihr das initiale Setup erfolgreich absolviert habt, könnt ihr euren Mac mit einem Ethernet-Kabel mit dem von euch konfigurierten "grünen" LAN-Anschluss (also dem Anschluss, dem ihr das interne Netzwerk zugeordnet habt) verbinden und anschließend die webbasierte Konfigurationsoberfläche von IPFire aufrufen. Dazu müsst ihr euch erinnern, welche IP-Adresse ihr dem internen Netzwerkanschluss der Firewall gegebenen habt (typischerweise sowas wie "192.168.0.1") und könnt anschließend im Browser die URL "https://<ip>:444" aufrufen. Ihr solltet per HTTP-Basic-Authentication nach den Zugangsdaten des "admin"-Account gefragt werden und anschließend auf der Startseite der Weboberfläche landen.

IPFire Weboberfläche IPFire Weboberfläche

Ist das der Fall? Dann herzlichen Glückwunsch! Ihr habt soeben die Firmware eures ALIX-Boards aktualisiert und anschließend erfolgreich IPFire darauf installiert. Die weitere Konfiguration lässt sich durch die moderne Weboberfläche ziemlich schnell erledigen.


Simples Failover-Cluster mit Floating IP realisieren

18.09.2015 yahe administration legacy linux security

Immer wieder kommt es vor, dass der Wunsch aufkommt, die Verfügbarkeit eines internen Dienstes zu erhöhen. Mitunter ist das das allererste Mal, dass die Beteiligten sich überhaupt mit dem Thema Clustering auseinandersetzen müssen. Leider herrscht immer noch die Meinung vor, dass Clustering ein komplexes Thema sei, das man am besten weit nach hinten schiebt. Dabei wird es umso einfacher, je früher man sich darum Gedanken macht. Denn dann kann man seine Anwendungen von vorne herein darauf vorbereiten, in einem Cluster lauffähig zu sein.

An dieser Stelle würde ich gern einmal eine simple Lösung zeigen, wie es möglich ist, ohne Einsatz eines Loadbalancers ein einfaches Failover-Cluster mit einer Floating IP (auch als virtuelle IP bezeichnet) zu realisieren.

In einer einfachen Form gibt es zwei Maschinen im Netzwerk, die die gleiche Aufgabe erfüllen und sich eine gemeinsame IP-Adresse teilen. Wenn eine der beiden Maschine startet, prüft sie, ob die gemeinsame IP-Adresse bereits verwendet wird. Ist das nicht der Fall, dann nimmt sie sich die IP-Adresse. Startet nun die zweite Maschine, so bekommt diese mit, dass die IP-Adresse bereits verwendet wird und schaltet sich in einen Wartezustand. In diesem prüft sie regelmäßig, ob die IP-Adresse noch verwendet wird. Sobald das nicht mehr der Fall ist, übernimmt sie die IP-Adresse und nutzt sie selbst.

Dieses Vorgehen kann man noch entsprechend gegen fälschliche IP-Adressübernahmen absichern. Beispielsweise sollte man mehr als nur einmal prüfen, ob die IP-Adresse noch verwendet wird, bevor man sie übernimmt. Ebenso kann man sich Protokolle überlegen, damit mehr als nur zwei Knoten im Failover-Cluster existieren können (z.B. durch die Einführung einer Knotengewichtung oder durch dynamische Reihenfolgen). Durch den Einsatz von STONITH-Mechanismen kann man zudem dafür sorgen, dass Clusterknoten mit einer Fehlfunktion das Cluster nicht stören, indem man sie gewaltsam aus dem Cluster entfernt. Aber für unser simples Beispiel geht es auch erstmal ohne.

Um einen einfachen, für den Client transparenten, Failover hinzukriegen, brauchen wir unter Linux die zwei Tools "ip" und "arping". Das "ip"-Tool ist standardmäßig vorhanden und wird dazu verwendet, die Floating IP an ein Netzwerkinterface zu binden (z.B. "eth0"). Das "arping"-Tool muss man hingegen erst installieren. Dabei ist Vorsicht geboten, denn es gibt zwei verschiedene Implementierungen, wobei uns nur die aus den "iputils" der Linux Foundation interessiert, denn nur diese kennt den Parameter "-U" für "Unsolicited ARP Requests" (auch "Gratuitous ARP Messages" oder "ARP Announcements" genannt). Sinn und Zweck dieser ARP Requests ist es, den angeschlossenen Systemen mitzuteilen, dass IP-Pakete, die an die Floating IP gerichtet werden, nun an ein anderes Gerät zu routen sind.

Die folgenden beiden Befehle sind unter Linux ausreichend, um eine IP-Adresse zu übernehmen (wobei ihr "<IP>" durch die Floating IP und "<Interface>" durch den Namen des Netzwerkinterfaces ersetzen müsst):

sudo /bin/ip addr add <IP>/32 dev <Interface>
sudo /usr/bin/arping -U -c 5 -q -I <Interface> <IP>

Ich habe mir testweise ein PHP-Skript geschrieben, das die oben beschriebene Funktionalität ausführt. Beim ersten Starten prüft es, ob die Floating IP ("192.168.123.10") in Verwendung ist und übernimmt diese. Der zweite Aufruf bekommt mit, dass die Floating IP in Verwendung ist und versetzt sich in den Wartemodus. Wenn die IP-Adresse frei werden sollte, übernimmt das wartende PHP-Skript die IP-Adresse. Ein Screencapture des Tests habe ich bei Youtube hochgeladen. Der permanent laufende Ping rechts oben zeigt, dass trotz des Herunterfahrens des ersten Rechners die IP-Adresse weiterhin verwendbar bleibt. Im CURL-Aufruf rechts unten sieht man, dass die HTTP-Anfragen zwar über dieselbe IP-Adresse gestellt, jedoch von verschiedenen Servern beantwortet werden.

Mit dem gleichen Know-how kann man übrigens auch Angriffe auf Netzwerkdienste fahren, indem man die Pakete, die an den Dienst gerichtet sind, über den eigenen Rechner umleitet.


Accounts auf Windows 7 Loginseite verstecken

15.09.2015 yahe administration legacy security windows

Zur Einschulung meines Neffen habe ich ihm einen aufgearbeiteten Computer geschenkt. Als Sechsjähriger sollte er natürlich nur einen begrenzten Zugriff auf Systemfunktionen (Administration, Softwareinstallation, etc.) haben, was über einen eingeschränkten Account realisiert werden sollte. Gleichzeitig sollte es aber möglich sein, dass er den Rechner einschaltet kann und beim Hochfahren sofort in seinen Account eingeloggt wird. Das wiederum funktioniert nur, wenn es auf der Loginseite nur einen Account gibt, der ohne Passwort angelegt wurde. Also musste eine Lösung her, in einem eingeschränkten Account viel Administration unterbinden zu können und gleichzeitig nur einen Account auf der Windows-Loginseite zu haben.

Mein erster Schritt war, den vorhandenen, aber nicht sichtbaren, Administratoraccount in Windows 7 zu aktivieren. Dazu gebt ihr in das Suchfeld des Startmenüs die Buchstaben "cmd" ein. In der Liste darüber sollte euch die Kommandozeile als Ergebnis angezeigt werden. Macht einen Rechtsklick auf den Eintrag mit der Kommandozeile und führt sie als Administrator aus. In der Kommandozeile gebt ihr dann folgenden Befehl ein:

net user administrator /active:yes

Nun kann man sich als Administrator am System einloggen und ihm zum Beispiel ein Passwort geben. Weiterhin kann man nun den einzuschränkenden Account ordentlich konfigurieren (z.B. die Jugendschutzoptionen aktivieren). Leider hat man nun auf der Windows-Loginseite auch den Administrator-Account, der den automatischen Login verhindert. Um diesen wieder zu verstecken, können wir ein wenig Registry-Magie einsetzen. Wir erstellen uns eine "hide-admin.reg"-Datei mit folgendem Inhalt:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList]
"Administrator"=dword:00000000

Ebenso erstellen wir uns eine "show-admin.reg"-Datei mit diesem Inhalt:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList]
"Administrator"=dword:00000001

Dazu basteln wir uns noch zwei Batch-Dateien. Zuerst die "hide-admin.bat"-Datei:

runas /user:administrator "regedit hide-admin.reg"

Als zweites noch die "show-admin.bat"-Datei:

runas /user:administrator "regedit show-admin.reg"

Wenn wir nun mit dem eingeschränkten Account eingeloggt sind und den Administrator-Account auf der Windows-Loginseite verstecken wollen, rufen wir einfach die "hide-admin.bat"-Datei per Doppelklick auf. In der sich öffnenden Kommandozeile werden wir nach dem Passwort des Administrator-Accounts gefragt, das wir noch mit Enter bestätigen. Anschließend ist der Administrator-Account auf der Windows-Loginseite versteckt und beim Hochfahren loggt sich das System automatisch in den eingeschränkten Account ein.

Sollten wir nun administrative Aufgaben wahrnehmen müssen, rufen wir einfach im eingeschränkten Account die "show-admin.bat"-Datei auf, geben das Administrator-Passwort ein und bestätigen mit Enter. Anschließend können wir den Benutzer wechseln, die administrativen Aufgaben erledigen (z.B. Updates installieren, Software installieren, Konfigurationsänderungen vornehmen, etc.), danach wieder in den eingeschränkten Account wechseln und den Administrator-Account wieder verstecken.


WordPress mit TLS Client Authentication absichern

27.04.2015 yahe administration code legacy security wordpress

Vor einiger Zeit hatte ich einmal gezeigt, wie man WordPress gegen eine größere Menge von Zero-Day-Exploits absichern kann, indem man verschiedene Datenbanknutzer einführt. Dadurch verwenden normale Webseitenbesucher einen Datenbanknutzer mit geringeren Privilegien und Nutzer des Backends einen Datenbanknutzer mit höheren Privilegien. Bei Softwarefehlern, die schreibenden Zugriff auf die Datenbank ermöglichen, können so Schreibversuche unterbunden werden.

Leider ist den Entwicklern von WordPress nicht sonderlich daran gelegen, die Datenbank so zu strukturieren, dass bestimmte Datenbankzugriffe für gewisse Nutzer nicht notwendig sind. So wurde vor einiger Zeit ein neues Session-Management eingeführt, das es zwar ermöglicht, Sitzungen auf anderen Rechnern zu beenden, dafür jedoch Daten in eine Tabelle schreibt, in der auch wichtige Konfigurationsinformationen abgelegt sind. Das führte zu Problemen bei der Trennung der Datenbanknutzer. Ich überlegte deshalb, wie ich die Trennung der Datenbanknutzer beibehalten und gleichzeitig flexibler bestimmen kann, wann ein höher privilegierter Datenbanknutzer benötigt wird.

Ebenfalls vor einiger Zeit hatte ich einmal gezeigt, wie man in NGINX TLS-Clientzertifikate einsetzen kann. Meine Idee war nun, die Steigerung der Privilegien innerhalb der WordPress-Datenbank an die Nutzung von TLS-Clientzertifikaten zu koppeln. So hat man eine Art Zwei-Faktor-Authentisierung: Mit seinem Zertifikat schaltet man die Nutzerrechte in der Datenbank frei und mit seinem Passwort loggt man sich im Backend ein.

Mein erster Versuch war, mit optionalen Clientzertifikaten zu arbeiten. Der Server fragt die Clientauthentisierung beim Browser an. Wenn der Browser kein Clientzertifikat zurückliefert, wird trotzdem mit der Seitendarstellung mit geringeren Priviliegen fortgefahren. Allerdings stellte sich heraus, dass der Safari-Browser bei jedem einzelnen Seitenaufruf nach dem Clientzertifikat fragt, sobald man eines hinterlegt. Eigentlich sendet der Server das Zertifikat der CA mit, von der das passende Clientzertifikat ausgestellt sein muss. Browser können diese Information nutzen, um die Clientzertifikate herauszufiltern, die nicht verwendet werden können. Doch leider sieht das Apple anders und so fragt der Safari den Nutzer ständig nach dem zu verwendenden Clientzertifikat, selbst, wenn es kein passendes gibt. Also musste eine nutzerverträglichere Lösung her.

In der neuen Lösung ruft man eine separate Seite auf, für die man zwingend eine Clientauthentisierung durchführen muss. Diese überprüft die Authentizität des Zertifikats und leitet dann auf einen Teil der eigentlichen, zu schützenden Seite weiter. Dieser Teil startet die Session, in der hinterlegt wird, dass die höheren Privilegien genutzt werden dürfen. Der Aufruf dieses Teils ist durch ein zeit- und passwortbasiertes Challenge-Response-Verfahren geschützt. Das spannende an diesem Ansatz ist, dass es prizipiell auch dafür geeignet ist, ein Single-Sign-On-Portal für verschiedenste Anwendungen aufzubauen. Kern der Anwendung ist die Datei "auth.php", in der sich die grundlegende Logik befindet:

<?php
  define("SUCCESS_CONTENT", "SUCCESS"); // defines what text should be in TLS_SUCCESS

  define("CHALLENGE_ALGO",   "sha256"); // defines the algorithm used for response HMAC
  define("CHALLENGE_LENGTH", 32);       // defines the length of the random challenge

  define("PARAM_START",   "?"); // should be static
  define("PARAM_VALUE",   "="); // should be static
  define("PARAM_COMBINE", "&"); // should be static

  define("CHALLENGE_PARAM", "challenge"); // name of challenge GET parameter
  define("RESPONSE_PARAM",  "response");  // name of response GET parameter

  define("DN_PARAM",      "TLS_DN");      // name of DN SERVER parameter
  define("SUCCESS_PARAM", "TLS_SUCCESS"); // name of success SERVER parameter

  define("LOGINSESSION_LOGGEDIN", "loginsession_loggedin");

  function getChallenge() {
    return openssl_random_pseudo_bytes(CHALLENGE_LENGTH);
  }

  function getResponse($challenge, $previous = false) {
    // prepare a delta time value for cases where we missed the variance window
    $delta = ($previous) ? CHALLENGE_VARIANCE : 0;

    // use the current time as a replay prevention mechanism
    $time_challenge = dechex((int)((time()-$delta) / CHALLENGE_VARIANCE));

    return hash_hmac(CHALLENGE_ALGO, $challenge.$time_challenge, CHALLENGE_KEY);
  }

  function check_GET() {
    $result = false;

    // check if the parameters are set
    if (isset($_GET[CHALLENGE_PARAM]) && isset($_GET[RESPONSE_PARAM])) {
      // convert challenge parameter to binary
      $challenge = hex2bin($_GET[CHALLENGE_PARAM]);
      // check if the challenge parameter was a hex
      if (false !== $challenge) {
        // calculate response from challenge parameter
        $response = getResponse($challenge, false);
        // check if response calculation was possible
        if (false !== $response) {
          // check if the calculated response matches the response parameter
          $result = (0 === strcasecmp($response, $_GET[RESPONSE_PARAM]));
        }

        // maybe we just passed the time variance window, test the previous one
        if (!$result) {
          // calculate response from challenge parameter
          $response = getResponse($challenge, true);
          // check if response calculation was possible
          if (false !== $response) {
            // check if the calculated response matches the response parameter
            $result = (0 === strcasecmp($response, $_GET[RESPONSE_PARAM]));
          }
        }
      }
    }

    return $result;
  }

  function check_SERVER() {
    $result = false;

    // check if the necessary TLS server values have been set
    if ((isset($_SERVER[DN_PARAM])) && (isset($_SERVER[SUCCESS_PARAM]))) {
      // check if the TLS server values contained the required content
      $result = ((0 === strcasecmp(DN_CONTENT,      $_SERVER[DN_PARAM])) &&
                 (0 === strcasecmp(SUCCESS_CONTENT, $_SERVER[SUCCESS_PARAM])));
    }

    return $result;
  }

  function createSession() {
    // open a session
    session_start();

    // session should exist now
    if (PHP_SESSION_ACTIVE === session_status()) {
      // prevent session fixation
      session_regenerate_id(true);

      $_SESSION[LOGINSESSION_LOGGEDIN] = true;
    }
  }

  function destroySession() {
    // check if there actually is a session to destroy
    if ((isset($_COOKIE[session_name()])) || (isset($_GET[session_name()]))) {
      // open the existing session
      session_start();

      // session should be open now
      if (PHP_SESSION_ACTIVE === session_status()) {
        // remove loggedin value from session
        if (isset($_SESSION[LOGINSESSION_LOGGEDIN])) {
          unset($_SESSION[LOGINSESSION_LOGGEDIN]);
        }

        // check if the session is empty
        if (0 === count($_SESSION)) {
          // destroy the session if it is
          session_unset();
          session_destroy();

          // unset the session cookie
          if (isset($_COOKIE[session_name()])) {
            $params = session_get_cookie_params();
            setcookie(session_name(), "", time()-3600, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
          }
        } else {
          // prevent session fixation
          session_regenerate_id(true);
        }
      }
    }
  }

  function doRedirect($target) {
    header("HTTP/1.1 302 Moved Temporarily");
    header("Location: ".$target);
  }

  function triggerChallengeResponse() {
    // generate new challenge
    $challenge = getChallenge();
    // check if challenge generation worked
    if (false !== $challenge) {
      // calculate response from challenge
      $response = getResponse($challenge, false);
      // check if response calucation worked
      if (false !== $response) {
        // convert challenge from binary to hex
        $challenge = bin2hex($challenge);

        // redirect to challenge-response
        doRedirect(PARAM_PATH.PARAM_START.CHALLENGE_PARAM.PARAM_VALUE.$challenge.PARAM_COMBINE.RESPONSE_PARAM.PARAM_VALUE.$response);
      }
    }
  }
?>

Die eigentliche Aufgabe, das Clientzertifikat zu prüfen, zur eigentlichen Webseite zu redirecten und dort die Session zu starten, übernimmt die Datei "logon.php". Diese gibt es zwei Mal, einmal auf dem Server, der die TLS-Clientauthentisierung übernimmt und einmal auf dem Server, auf dem WordPress installiert ist. Die in der Datei enthaltene Konfiguration sollte auf beiden Servern identisch sein:

<?php
  define("DN_CONTENT", "/cn=example.com"); // defines what text should be in TLS_DN

  define("CHALLENGE_VARIANCE", 300); // defines how long the challenge will be usable
  define("CHALLENGE_KEY",      "V3RY 5TR0NG P455W0RD"); // defines the password used for the response HMAC

  define("PARAM_PATH", "https://example.com/auth/logon.php"); // redirect path

  define("FINAL_PATH", "https://example.com/wp-login.php");

  require_once(dirname(__FILE__)."/auth.php");

  if (check_GET()) {
    createSession();
    print("<a href=\"".FINAL_PATH."\">Logon</a>");
  } else {
    if (check_SERVER()) {
      triggerChallengeResponse();
      exit();
    }
  }
?>

Es kann auch sinnvoll sein, die erhaltenen Privilegien wieder abzugeben, indem man die Session beendet. Diese Aufgabe übernimmt die Datei "logoff.php", die ebenfalls auf beiden Servern vorhanden und dieselbe Konfiguration enthalten sollte:

<?php
  define("DN_CONTENT", "/cn=example.com"); // defines what text should be in TLS_DN

  define("CHALLENGE_VARIANCE", 300); // defines how long the challenge will be usable
  define("CHALLENGE_KEY",      "V3RY 5TR0NG P455W0RD"); // defines the password used for the response HMAC

  define("PARAM_PATH", "https://example.com/auth/logoff.php"); // redirect path

  require_once(dirname(__FILE__)."/auth.php");

  if (check_GET()) {
    destroySession();
    print("logoff");
  } else {
    if (check_SERVER()) {
      triggerChallengeResponse();
      exit();
    }
  }
?>

In der eigentlichen Anwendung kann dann anhand der Session zwischen den Datenbanknutzern unterschieden werden. Im Falle von WordPress habe ich das in der Konfigurationsdatei "wp-config.php" getan:

$loginsession_evaluated = false;
// open the session if there is one
if ((isset($_COOKIE[session_name()])) || (isset($_GET[session_name()]))) {
  session_start();

  if ((isset($_SESSION["loginsession_loggedin"])) && ($_SESSION["loginsession_loggedin"])) {
    $loginsession_evaluated = true;
  }
}

// select user dependent on loginsession
if (($loginsession_evaluated) || (defined("DOING_CRON"))) {
  define("DB_USER",     "<ADMIN USER>");
  define("DB_PASSWORD", "<ADMIN PASSWORD>");
} else {
  define("DB_USER",     "<GUEST USER>");
  define("DB_PASSWORD", "<GUEST PASSWORD>");
}

Da das von der Theorie her vielleicht alles ein bisschen trocken ist, habe ich den Ablauf eines vollständigen Logins als Screencapture aufgenommen und bei Youtube hochgeladenen. Vielleicht wird dadurch klarer, wie die einzelnen Schritte ineinander greifen.

Ich hoffe, dieser kleiner Exkurs bietet dem ein oder anderen eine neue Lösungsmöglichkeit für die Absicherung von Nutzerzugängen. Die gezeigte Implementation habe ich, soweit wie möglich, generisch gehalten. Durch das Challenge-Reponse-Verfahren ist sie beispielsweise auch als Grundlage für Inter-Domain-Single-Sign-On geeignet und durch die Verwendung von einfachen Sessions sollte sie zudem softwareunabhängig einsetzbar sein.


Search

Links

RSS feed

Categories

administration (40)
arduino (12)
calcpw (2)
code (33)
hardware (16)
java (2)
legacy (113)
linux (27)
publicity (6)
review (2)
security (58)
thoughts (21)
windows (17)
wordpress (19)