In diesem Artikel schauen wir uns an, wie man den Single Sign-On-Dienst von Microsoft mithilfe von Firebase Cloud Messaging um eine Multi-Faktor-Authentifizierung mittels Push-Benachrichtigung erweitern kann.
Authentifizierungen sind integraler Bestandteil der digitalen Welt; sie kommen beim Onlinebanking, in den sozialen Netzwerken oder beim Anmelden im Firmennetzwerk zum Einsatz. Sie dienen dem Nachweis einer Identität.
Es hat sich allerdings gezeigt, dass ein großer Teil der Bevölkerung bei eben diesen Authentifikationen bei unterschiedlichen Diensten dasselbe oder ein nur leicht verändertes Passwort verwendet. Dieses Verhaltensmuster ist der Grund, weshalb immer mehr Websites und Firmen auf eine mehrstufige Authentifizierung setzen und ihre Nutzer:innen nach Eingabe des Passwortes um mindestens einen weiteren Faktor beim Anmeldeprozess bitten. Das geschieht beispielsweise durch das Auffordern zur Eingabe einer Zahlenfolge, welche dem Benutzer an seine hinterlegte E-Mail-Adresse zugesandt wird.
Darüber hinaus kann durch den Einsatz eines Single Sign-On die Authentifizierungsstelle zentralisiert werden und damit die Anzahl an benötigten Passwörtern pro Nutzer:in innerhalb einer Organisation auf ein einziges Passwort reduziert werden.
Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) ermöglicht das Senden von cross-plattform Nachrichten für iOS, Android und den Browser.
Die von Google entwickelte Cloud-Messaging-Lösung unterteilt ihre Architektur in drei notwendige Komponenten. Die erste Komponente, in der Abbildung ganz links stehend, ist für das Erstellen der Push-Benachrichtigung zuständig. Sie verpackt in einem Request an das FCM Backend welchen Inhalt, welche(n) Adressat(en) und weitere Konfigurationsmöglichkeiten die Benachrichtigung enthalten soll. Ein solcher Request kann entweder mithilfe der Notification Console GUI, einer grafischen Testumgebung von FCM oder eines eigenen Servers versendet werden.
Wird sich, wie im Fall dieser Arbeit, für ein eigenes Backend entschieden, so besteht die Möglichkeit die Anfrage an das FCM Backend per HTTP oder XMPP-Protokoll zu senden oder das Firebase Admin SDK zu verwenden. Benachrichtigungen können an einzelne Geräte oder an Gerätegruppen, welche sich für ein bestimmtes Thema (engl. topic) eingeschrieben haben, gesendet werden. Beim Firebase Admin SDK handelt es sich um eine Reihe von Bibliotheken zur Interaktion mit Firebase.
Die zweite Komponente, die zur Verwendung von FCM notwendig ist, wird von Firebase selbst zur Verfügung gestellt und zwar handelt es sich dabei um das FCM Backend. Es stellt u. a. einen Endpunkt zur Verfügung, an welchen der zuvor beschriebene Request gesendet werden muss. Darüber hinaus ist es für die Verteilung der Nachrichten an die entsprechende Transportschicht (engl. transport layer) zuständig. FCM unterstützt das Senden von Benachrichtigungen an Smartphones mit iOS oder Android-Betriebssystem und Browser, welche die Push API implementieren. Die unter Android verwendete Transportschicht ist die Android Transport Layer, unter iOS wird APNs verwendet. Im Web steht Web Push unter Verwendung des Web Push Protokolls zur Verfügung.
Zuletzt braucht man eine Applikation, welche das Firebase SDK integriert. Das SDK bietet bspw. die Funktionen zum Generieren des Registrierungstoken, sowie der Interaktion mit eingehenden Benachrichtigungen.
Active Directory Federation Services
Active Directory Federation Services (AD FS) ist Microsofts Single Sign-On (SSO) Dienst für Windows Server.
SSO beschreibt hierbei eine Architektur, bei der sich Benutzer bei einer zentralen Authentifizierungsquelle authentifizieren und dadurch ebenfalls bei allen Diensten, welche diese Quelle als vertrauenswürdig hinterlegt hat, ohne weitere Interaktion, authentifiziert sind.
In einer Firma bedeutet das, dass ein Mitarbeiter, welcher in seinem Arbeitsalltag beispielsweise auf ein E-Mail-Programm, einen Online-Kalender und eine Anwendung zur Zeiterfassung zugreifen muss, sich statt jeweiliger Anmeldung an allen drei Systemen, sich nur an einer Stelle authentifiziert und anschließend auf alle genannten Systeme Zugriff hat. Voraussetzung ist, wie bereits erwähnt, dass alle weiteren Dienste bei der zentralen Stelle als vertrauenswürdige Quellen hinterlegt sein müssen.
Für die Verwendung des AD FS stehen verschiedene Protokolle, wie SAML. OpenID Connect oder OAuth zur Verfügung. Außerdem erlaubt AD FS die Integration von Azure MFA, Microsofts Cloud-basierter MFA, oder einer selbst implementierten MFA-Lösung. Eine selbst implementierte MFA-Lösung, wie sie bei inovex zum Einsatz kommt, wird im AD FS Kontext häufig als Adapter bezeichnet und beruht auf der Implementierung einer .NET Klassenbibliothek (engl. class library) , welche anschließend im AD FS eingebunden wird. Eine Anleitung, wie ein solcher Adapter erstellt wird findet ihr hier.
Proof of Concept
Im Proof of Concept (PoC) dieser Arbeit wurde Firebase Cloud Messaging verwendet, um Push-Benachrichtigungen vom AD FS an Geräte zu schicken, welche sich zuvor registriert haben. Die Benachrichtigung wird mithilfe einer Capacitor-App und dem Firebase SDK empfangen und angezeigt. Die Kommunikation von der App in Richtung AD FS läuft über einen WebSocket.
Backend
Der Server, zum Senden der Benachrichtigung an die entsprechenden Geräte, muss ein Windows Server sein, denn AD FS ist ein Dienst, welcher nur auf Windows Servern zur Verfügung steht und die Authentifizierung anhand der Daten im Active Directory vornimmt. AD FS steht ab Windows Server 2003 R2 zur Verfügung. Der PoC dieser Arbeit beruht auf einem Windows Server 2016 mit installierter AD FS-Rolle. Eine Anleitung zum Aufsetzen eines Windows Servers und zur Installation der AD FS-Rolle findet ihr unter [2] [3] . Ist der Windows Server und auch die AD FS-Rolle installiert, dann muss der Adapter (weiter oben mit Link) implementiert und anschließend in den AD FS geladen werden.
Zum Senden der Benachrichtigung muss der AD FS-Adapter einen Request an das FCM Backend schicken. So ein Request könnte mithilfe von HTTP v1 wie folgt aussehen:
1 2 3 4 5 6 7 8 9 10 11 12 |
curl -H 'Authorization: Bearer ' -H 'Content-Type: application/json' -d '{"token" : "", "notification": { "title": "inovex", "body": "Trying to sign in?" }, "data": { "queueName": } }' https://fcm.googleapis.com/v1/projects//messages:send |
Für das Anzeigen einer Benachrichtigung ist dieser HTTP-Post-Request alles was serverseitig benötigt wird. Er beinhaltet ein oauth2.0-Token, welches euren Server gegenüber Firebase authentifiziert. In der Payload wird ein Token mit gesendet, dass die Registrierungs-ID eines Gerätes beinhaltet, dass sich zuvor für Push-Benachrichtigungen bei eurer Firebase-App registriert hat. Damit Firebase weiß zu welcher App das Registrierungstoken gehört ist der Endpunkt, an den dieser Request geht, eindeutig an euer Projekt in Firebase gekoppelt. Die <project-id> entspricht also der ID, die man in FCM beim Erstellen eines Projektes erhält. Auf das optionale „data“-Objekt und die Bedeutung des Attributes „queueName“ kommen wir später noch zu sprechen.
Um eine Antwort der Applikation empfangen zu können wurden in dieser Arbeit WebSockets verwendet. Das ist möglich, denn der AD FS lässt es zu seine Anmeldemaske in einem gewissen Rahmen selbst zu programmieren. Im HTML der vom AD FS ausgelieferten Website wurde die Funktionalität von WebSockets implementiert. Nach dem Absenden der Push-Benachrichtigung wird die Funktion createWebSocket() aufgerufen. Sie ermöglicht es uns ein Ablehnen bzw. Bestätigen der Push-Benachrichtigung mitzubekommen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function createWebSocket() { let webSocket = $("#ws").val(); let token = $("#authToken").val(); let header = "Token=" + token; let totp; let socket; // Websocket uses TLS so query parameter is not accessible to anyone socket = new WebSocket(`${webSocket}?${header}`); socket.onmessage = function (event) { totp = JSON.parse(event.data).totp; if (totp) { $("#challengeQuestionInput").val(totp); $("#submitButton").click(); } }; socket.onclose = function (event) { socket.close(); }; } |
Firebase Cloud Messaging
Zu Beginn muss in FCM ein neues Projekt angelegt werden. Diesem Projekt wird die oben beschriebene <project-id> entnommen. Innerhalb des Projektes muss dann für jede Plattform (Android, iOS, Web), an die Benachrichtigungen geschickt werden sollen, eine App-Instanz erstellt werden. In der FCM Doku findet man dazu eine detaillierte Anleitung.
Empfängt Firebase einen HTTP-Request eures Server, wie im Abschnitt davor beschrieben, dann kann es anhand der Projekt-ID und des Registrierungstoken herausfinden, an welches Gerät die Benachrichtigung geschickt werden soll und über welche Transportschicht. FCM übernimmt dann das Zustellen der Nachricht sobald das Zielgerät online ist. Ist das Gerät nicht online wird die Nachricht in eine Warteschlange gepackt und zugestellt sobald das Gerät wieder online ist. Hier besteht die Möglichkeit, über ein TTL (Time to Live)-Attribut im Request anzugeben, wie lange versucht werden soll, die Nachricht zuzustellen, bevor sie verworfen wird.
Applikation
Bei der in diesem Artikel verwendeten App handelt es sich um eine Angular-App, welche mithilfe der Capacitor Laufzeitumgebung von Ionic sowohl im Browser als auch als native App unter iOS und Android verwendet werden kann.
Im ersten Schritt muss die App sich bei FCM registrieren. Das Capacitor-Plugin für FCM stellt dafür die Funktion requestPermissions() bereit. Die Funktion fragt zunächst die Berechtigung zum Senden von Push-Benachrichtigungen an und registriert die App-Instanz dann bei FCM. Im Event-Handler „registration“ kann dann der von Firebase ausgestellte Registrierungstoken erhalten werden. Dieser Token ist pro App-Instanz eindeutig und sollte geheim gehalten werden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Request permission to use push notifications // iOS will prompt user and return if they granted permission or not // Android will just grant without prompting PushNotifications.requestPermissions().then((result) => { if (result.receive === 'granted') { // Register with Apple / Google to receive push via APNS/FCM PushNotifications.register(); } else { // Show some error } }); PushNotifications.addListener('registration', (token: Token) => { alert('Push registration success, token: ' + token.value); }); |
Die App ist nun bereit Push-Benachrichtigungen zu empfangen. Ist die Applikation im Hintergrund oder geschlossen, dann übernimmt das Firebase SDK das Anzeigen der Benachrichtigung. Ist die App im Vordergrund, ist sie selbst für das Anzeigen einer passenden Meldung zuständig. Das Plugin stellt auch dafür einen Event-Listener zur Verfügung, welcher Zugriff auf die gesendete Payload ermöglicht. Die Capacitor-Dokumentation beinhaltet einen ausführlichen Artikel über das Integrieren von FCM in eine Angular App.
1 2 3 4 5 6 |
PushNotifications.addListener( 'pushNotificationReceived', (notification: PushNotificationSchema) => { alert('Push received: ' + JSON.stringify(notification)); } ); |
Um den Authentifizierungsvorgang abzuschließen haben wir uns dazu entschieden, ein TOTP (Time based One-Time Password) zu generieren, wenn der Benutzer den Anmeldevorgang bestätigt. Dieses TOTP wird mithilfe eines WebSockets an eine Message Queue geschickt. Diese Queue hat einen zufällig generierten Namen, welcher ebenfalls über die Payload vom AD FS mit gesendet wird. Die Anmeldeseite des AD FS lauscht ab dem Zeitpunkt des Absetzens des HTTP-Request auf dieser Queue. Bestätigt der Benutzer den Login-Vorgang durch klicken des „Yes“-Buttons (s. Demo Video), dann generiert die App einen TOTP und sendet ihn an die entsprechende Message Queue. Das Senden des TOTP entspricht auch wieder nur dem Senden eines HTTP-Post-Request. Wird der Anmeldevorgang abgelehnt, wird ein negativer Wert an die Message Queue geschickt und der Anmeldevorgang endet auf Seiten des AD FS mit einer Fehlermeldung.
1 2 3 |
curl -H 'X-Token: ' -d '{"totp": ""}' https://msgq.example.com/publish/ |
Demo-Video
Fassen wir kurz zusammen, was für die Demo notwendig war.
- Windows Server aufsetzen
- AD FS installieren
- AD FS-Adapter Code schreiben und integrieren
- Bei FCM ein Projekt erstellen
- In diesem Projekt für jede Plattform eine App erstellen
- Eine Angular App mit Capacitor cross-compilieren
- Das Capacitor-FCM-Plugin zum Empfangen der Benachrichtigung installieren
- HTTP-Request vom Server an FCM senden
- TOTP von der App aus über einen WebSocket an eine Message Queue schicken
- TOTP auf AD FS-Seite empfangen und validieren
Im Video ist zu sehen, wie die SSO-Seite des AD FS geöffnet wird. Es wird zuerst die Authentifizierung mit Benutzername und Passwort durchgeführt, bevor der AD FS den Browser zur MFA-Seite weiterleitet. Wählt der Benutzer dann die Option zum Senden einer Push-Benachrichtigung, sendet der AD FS den Request an FCM und die Anmeldeseite verbindet sich gegen den WebSocket. Hat FCM die Nachricht zugestellt, hat der Benutzer die Möglichkeit die Benachrichtigung durch einen Klick zu öffnen. Bestätigt er den Anmeldevorgang, wird das TOTP an die entsprechende Message Queue geschickt. Schließlich wird der Code des „onmessage„-Event-Handler der WebSocket-Verbindung ausgeführt und dadurch das TOTP in das Input-Feld eingefügt und die Form submitted . Die MFA ist damit abgeschlossen. Die Validierung des TOTP schlägt in diesem Fall fehl, da das in der Testumgebung erstellte TOTP aus Sicherheitsgründen ohne das Shared Secret berechnet wurde.