Programmierung > Delphi/Lazarus > FRITZ!Box

Anmeldung mit SessionID

Firmware xx.04.74 (Klasse bis v0.16)

Auf der FRITZ!Box sind etliche Dateien abgelegt. Auf einige kann nur zugegriffen werden, wenn man sich mit einem Passwort angemeldet hat. Mit der FRITZ!Box Firmware xx.04.74 hat AVM die SessionID eingeführt und das Login-Verfahren geändert.

Boxinfo

Eigenschaften

Es stehen folgende Eigenschaften zur Verfügung:

property IsLoggedIn: Boolean;
property LogIn: Boolean;
property LogOut: Boolean;
property Password: String;
property Session: TSession;

OUT IsLoggedIn: Nach Überprüfung der Sessiondaten erhält man die Information, ob die Anwendung an der FRITZ!Box angemeldet ist.

OUT LogIn: Es wird versucht, die Anwendung an der FRITZ!Box anzumelden. Als Ergebnis wird zurückgegeben, ob die Anwendung angemeldet ist.

OUT LogOut: Es wird versucht, die Anwendung von der FRITZ!Box abzumelden. Als Ergebnis wird zurückgegeben, ob die Anwendung noch angemeldet ist.

IN Password: Damit wird der Komponente das zu verwendende Passwort mitgeteilt.

OUT Session: Es werden die Sessiondaten zurückgegeben. Dies ist ein Record mit den Daten isWriteAccess, SID und Challenge.

isWriteAccess gibt an, ob die Anwendung angemeldet ist und "Schreibzugriff" besitzt. Möglich sind die Werte 0 und 1. SID ist die 64 Bit lange SessionID. Wenn die Anwendung nicht angemeldet ist, ist diese ungültig und hat den Wert 0000000000000000. Challenge ist ein String, welcher für die Verschlüsselung des Passwortes benötigt wird.

Anwendung

Beispiel für die Anwendung der Eigenschaften

procedure TfrmMain.sbnLoginLogoutClick(Sender: TObject);
var
  aLoggedIn : Boolean;
  aSession  : TSession;
begin
  {
  *  An- bzw Abmelden.
  }
  if fFritzBox.IsLoggedIn
  then begin
    aLoggedIn := fFritzBox.LogOut;
  end
  else begin
    fFritzBox.Password := edtPassword.Text;
    aLoggedIn := fFritzBox.LogIn;
  end;
  {
  *  Kontrolllampe einstellen.
  }
  imgRed.Visible   := not(aLoggedIn);
  imgGreen.Visible := aLoggedIn;
  {
  *  Sessiondaten holen und anzeigen.
  }
  aSession := FFritzbox.Session;
  lblSessionIWAValue.Caption       := aSession.isWriteAccess;
  lblSessionIDValue.Caption        := aSession.SID;
  lblSessionChallengeValue.Caption := aSession.Challenge;
end;

imgRed und imgGreen sind in der Demo zwei übereinander liegende TImages, welche als Kontrolllampe fungieren. lblSessionIWAValue, lblSessionIDValue und lblSessionChallengeValue sind TLabel in der Demo zur Anzeige der Sessiondaten.

Informationen

Die SessionID wird über die Seite login_sid.xml vergeben, welche über einen Winsock-Client als HTTP-Request abgerufen wird:

GET /cgi-bin/webcm?getpage=../html/login_sid.xml&sid=0000000000000000 HTTP/1.1
Host: fritz.box:80
Accept: text/xml
Keep-Alive: 115
Connection: Keep-Alive

Es soll nicht zwingend sein beim Aufruf der Seite die SessionID mit "&sid=0000000000000000" anzugeben. Jedoch musste ich feststellen, dass ohne Angabe dieser ungültigen SessionID die folgenden Anmeldeversuche stets fehlschlugen.

Die Antwort sieht zum Beispiel so aus:

HTTP/1.0 200 OK
Cache-Control: no-cache
Content-type: text/xml
Expires: -1
Pragma: no-cache
 
 
<SessionInfo>
  <iswriteaccess>0</iswriteaccess>
  <SID>0000000000000000</SID>
  <Challenge>37a070cc</Challenge>
</SessionInfo>

Die boolsche Variable iswriteaccess gibt an, ob man an der FRITZ!Box angemeldet ist. Die SID ist ein 64bit langer numerischer Wert. Wenn man nicht angemeldet ist, beträgt dieser 0000000000000000. Der Wert Challenge wird für die Verschlüsselung des Passwortes benötigt.

Aus dem Wert für Challenge und dem Passwort muss ein Response-Wert ermittelt werden. Dazu wird der Wert Challenge über einen Bindestrich mit dem Passwort verbunden. Da die Sessiondaten und das Passwort hier als Ansistrings vorliegen muss der String in einen UTF16LE-String umgewandelt werden. In der Komponente wird dabei nicht berücksichtigt, das Zeichen mit einem Unicode Codepoint über 255 aus Kompatibilitätsgründen mit dem Zeichen "." (UTF16LE: $2e00) dargestellt werden müssen. Zu diesem UTF16LE-String wird ein MD5Hash ermittelt. Dieser MD5Hash wird mit einem Bindestrich an die Challenge angehängt.

aLoginResponse := fSession.Challenge + '-' +
                  MD5Hash(AnsiToUtf16(fSession.Challenge + '-' + fPassword));

Die Anmeldung erfolgt wiederum über einen HTTP-Request:

POST /cgi-bin/webcm HTTP/1.1
Host: fritz.box:80
Accept: text/html
Keep-Alive: 115
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 104
 
getpage=../html/de/menus/menu2.html&login:command/response=
37a070cc-9d0268dcecb78cf314a98097350795bd

Die letzten beiden Zeilen sind eine Zeile und hier nur wegen der Länge getrennt. Die Antwort darauf ist eine Loginseite mit über 700 Zeilen und wird hier deshalb nicht vollständig wiedergegeben.

HTTP/1.0 200 OK
Cache-Control: no-cache
Content-type: text/html; charset=utf-8
Expires: -1
Pragma: no-cache
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                                   "http://www.w3.org/TR/html4/loose.dtd">
<html>
<!--loginPage-->
<head>
<title>FRITZ!Box Anmeldung</title>
...
<!-- Refresh Form -->
<input type="hidden" name="sid" value="8534f611850a8659" id="uiPostSid">
...
<!-- END Refresh Form -->
...
</body>
</html>

Aus dem Refresh Form wird die neue SessionID extrahiert. In der Komponente wird die login_sid.xml mit der neuen SessionID aufgerufen um den Wert für IsWriteAccess zu prüfen.

Links

Technical Note zum geänderten Login-Verfahren
Fritzbox Monitor Komponente Delphi 2010 inkl. Source Code von oCent