Es gibt verschiedene Möglichkeiten sich bei einem Online-Dienst zu authentifizieren, z.B. durch

  • Wissen (Benutzername und Kennwort)
  • Besitz (Smartcard, Smartphone etc.)
  • Biometrische Merkmale (Fingerabdruck, Gesichtserkennung etc.)

Dabei können weitere Faktoren hinzugezogen werden:

  • Standort (IP-Adresse, GPS-Daten)
  • Zeit (Zeitfenster)

Je nachdem wie viele Möglichkeiten miteinander kombiniert werden, spricht man von 2-, 3- oder allgemein Mehrfaktor-Authentifizierung (2FA, 3FA, MFA).

Vor- und Nachteile

Sicherheitsgewinn

  • Kennworte gelten in bestimmten Bereichen heute als nicht mehr als sicher genug (Phishing, Man-in-middle, Hacking, Ausspähen, schwache Kennworte, Brute force etc.)
  • Bei mehreren Diensten werden häufig gleiche Kennworte benutzt, was zum einen die Wahrscheinlichkeit erhöht, dass diese Zugangsdaten bekannt werden und der Schaden wird durch gleich mehrere gehäckte Systeme vergrößert.

Ein Angriff muss nun auf mehreren Ebenen gleichzeitig erfolgen. Das erhöht den Aufwand des Angreifers erhöht mit jedem weiten Faktor und senkt seine Erfolgswahrscheinlichkeiten stark ab

Zwar sollte man daher bei sicherheitsrelevanten Diensten vom Angebot einer 2-Faktor-Authentifizierung auf jeden Fall Gebrauch machen, dennoch müssen auch die Nachteile angesprochen werden.

Mehraufwand

Für den Nutzer

Der Anmeldevorgang wird verlängert und aufwändiger.
Verlust des 2. Faktors (z.B. Neuinstallation des Smartphones) macht Zugang unmöglich. Bestenfalls muss mit hohem Aufwand der 2. Faktor wieder hergestellt werden (Kontaktaufnahme mit Dienstbetreiber, Zurücksetzen des 2. Faktors). Dies bietet im Übrigen auch wieder Angriffsvektoren für Hacker. Daher sollte man sich jeweils einen weiteren "2. Faktor" hinterlegen (z.B. Wiederherstellung-Token, 2. Gerät mit Freischaltung, ...)

Für den Anbieter

Für den Anbieter ist der Mehraufwand zum Einen bei der Entwicklung mit entsprechenden einmaligen Kosten größer. Zum Anderen kann der Mehraufwand während des Betriebs sehr viel höher werden. Das liegt daran, das nun mehrere Faktoren verloren gehen können. Je nach Sicherheitsgrad müssen z.B. Hotlines oder schriftliche Wiederherstellungsverfahren bereitgestellt werden.

TOTP

Das in der Beispielanwendung verwendete Time-based One-Time Password TOTP- Verfahren  ist kostengünstig. Es fallen keine Gebühren für SMS-Versand oder Kosten für spezielle weitere Geräte wie z.B. SmartCard o.ä. an.

Der zu verifizierende 2. Faktor, der Authentifikation-Code (TOTP-Code) ist von Schlüssel und der Zeit abhängig:

\( TOTP{-}Code = f(Key, Time) \)

Das bedeutet, das ein einmalig generierter Schlüssel auf dem Server und einem 2. Gerät (Smartphone) zur Generierung hinterlegt wird. Dieser Schlüssel wird abgelesen und von Hand oder mittels eines QR-Codes Im Klartext auf das 2. Gerät übertragen. Die Zeit muss auf beiden Geräten natürlich einigermaßen gleich sein, den der TOTP-Code wird in der Standardeinstellung alle 30 Sekunden geändert.

Das folgende UML-Aktivitätsdiagram (nach dem UML1.3-Standard) veranschaulicht die Komplexität der Anmeldung der Beispielanwendung. Dabei ist der Workflow noch unvollständig, denn die das Zurücksetzen des TOTP-Keys usw. ist noch nicht möglich:

UML1-Akivitätsdiagramm für die Benutzeranmeldung mit 2FA mit TOTP
UML1.3 - Akivitätsdiagramm für die Benutzeranmeldung mit 2FA mit TOTP

Das zweistufige Anmeldeverfahren, wird im Programmcode simpleAuthService.py im Service-Teil im durch 2 Routen repräsentiert:

Login 1. Faktor

@app.route('/auth/user/login1', methods=['POST'])                   # check username and pw

 
Struktogramm: Login 1. Faktor
Struktogramm: Login 1. Faktor
 
JWT enthält folgende decodierte Nutzlast (Payload):
 
{<br>  "exp": 1773252794, <br>  "userId": 1<br>}
Damit kann noch keine Autorisierung vorgenommen werden.
 
Login 2. Faktor

@app.route('/auth/user/login2', methods=['POST'])                    # check 2fa totp

 

Struktogramm: Login 2. Faktor

Struktogramm: Login 2. Faktor

Erst jetzt ist JWT-Payload:

{<br>   "exp": 1773253140,<br>   "roleIDs": [1, 2]<br>}

Die Rollen zur Autorisierung sind jetzt enthalten.

Die beiden Stufen des Logins werden über die Route des Dashboard aufgerufen:


185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
223
224
...
@app
.route('/dashboard', methods=['POST', 'GET']) def dashboard(): if request.method == 'POST': # called by form data of login.html factor = request.form['factor'] if factor == "1_factor": # loginUser1 returns i.e. jwt (decoded) ('{"token": {"exp": 1773259989, "userID": 1}}', 200) jsonResponse = json.loads(loginUser1()[0]) if jsonResponse['token'] == '-1': # authentification failed -> login form return render_template('login.html', message = "Wrong username/password.") else: if jsonResponse['totpActivated'] == 0: # totp not yet activated -> send qrcode # do not save qrcode image -> safe it into memory and give it in form of encoded data to template: # https://stackoverflow.com/questions/70199318/django-otp-totp-how-to-display-qr-code-in-template/73567883#73567883 qr_code_img = qrcode.make(jsonResponse['uri']) # This should be the device for which you want to generate the QR code buffer = BytesIO() qr_code_img.save(buffer) buffer.seek(0) encoded_img = b64encode(buffer.read()).decode() qr_code_data = f'data:image/png;base64,{</span>encoded_img<span style="background-color: #eee;">}' resp = make_response(render_template("qrcode.html", qr_code_data = qr_code_data)) resp.set_cookie('token', jsonResponse['token'], httponly=True, secure=True) return resp # load qrcode form else: # totp already activated -> check totp code resp = make_response(render_template("checkTotp.html")) resp.set_cookie('token', jsonResponse['token'], httponly=True, secure=True) return resp # load check totp form if factor == "2_factor": # loginUser2 returns i.e. jwt (decoded) ('{"token": {"exp": 1773259989, "roleIDs": [1, 2]}}', 200) jsonResponse = json.loads(loginUser2()[0]) if jsonResponse['token'] == '-1': # 2fa failed -> login form return render_template('login.html', message = "Wrong authentification code.") else: return standard_response("dashboard.html",token=jsonResponse['token']) # 2fa successful -> load dashboard if request.method == 'GET': # links or redirected by login.html (already logged on) return standard_get_response("dashboard.html") ...

Auszug: simpleAuthService

Welcher dabei von beiden Logins aufgerufen wird, hängt vom HTML-Formular ab, von dem der Aufruf der Dashboard-Route erfolgt:

<h1>Log-in</h1><br>
<p class='message'>{{message}}</p>
<form id="myForm" action = "/dashboard" method = "post">
    <input type="hidden" name="factor" value="1_factor" >
    <input id='username' type = "text" name = "username" value="testUser" placeholder="Your Username">
    <input id='password' type = "password" name = "password" value="testPwd" placeholder="Your Password"/>
    <input type="submit" name="login" class="login login-submit" value="Login">
    <a class="button" href="/googleLogin">Google Login</a>
</form>

Auszug: login.html

oder

<h1>Authentication Code</h1><br>
<form id="myForm" action = "/dashboard" method = "post">
    <input type="hidden" name="factor" value="2_factor" >
    <input type = "text" name = "totpCode" placeholder = "Your Authentication Code">
    <input type = "submit" name = "login" class = "login login-submit" value="Login">
</form>

Auszug: checkTotp.html

Entscheidend ist dabei das "hidden input field" mit der Bezeichnung factor.

Last modified: Wednesday, 11 March 2026, 10:00 PM