Multi-Faktor-Authentifizierung technisch umsetzen
OWASP
0x00
Security · MFA · TOTP · WebAuthn
Multi-Faktor-Authentifizierung technisch umsetzen
TOTP, WebAuthn und Magento Two-Factor Auth im Detail

Ein gestohlenes Admin-Passwort reicht heute für einen Totalschaden im Magento-Backend. Dieser Artikel zeigt, wie TOTP nach RFC 6238, gehashte Backup-Codes und phishing-resistentes WebAuthn/FIDO2 technisch korrekt implementiert werden und wie sich Magentos natives Two-Factor-Auth-Modul für alle Admin-Nutzer erzwingen lässt.

14 Min. Lesezeit RFC 6238 · Backup-Codes · WebAuthn/FIDO2 Magento_TwoFactorAuth · PHP 8.4

1. Warum Passwörter allein nicht mehr ausreichen

Passwörter allein sind seit Jahren keine ausreichende Absicherung mehr für administrative Zugänge. Credential-Stuffing-Angriffe nutzen Milliarden geleakter Zugangsdaten aus früheren Breaches vollautomatisch aus, und Phishing-Kits imitieren Login-Formulare inzwischen pixelgenau. Für ein Magento-Backend mit Zugriff auf Kundendaten, Zahlungsinformationen und die komplette Shop-Konfiguration ist ein kompromittiertes Admin-Passwort ein Totalschaden-Szenario, keine Randnotiz.

Multi-Faktor-Authentifizierung (MFA) begegnet diesem Problem, indem sie mindestens zwei unabhängige Faktoren aus unterschiedlichen Kategorien kombiniert: Wissen (Passwort), Besitz (Smartphone, Hardware-Token) und Inhärenz (Fingerabdruck, Gesichtserkennung). NIST SP 800-63B verlangt für höhere Assurance-Level explizit mindestens zwei dieser Kategorien, weil ein Angreifer selten gleichzeitig ein gestohlenes Passwort und physischen Zugriff auf ein zweites Gerät besitzt. Die folgenden Abschnitte behandeln die technische Umsetzung der beiden praxisrelevantesten Verfahren, TOTP und WebAuthn, sowie deren native Integration in Magentos Two-Factor-Auth-Modul.

2. TOTP-Mechanik nach RFC 6238: Secret, Zeitfenster, HMAC-SHA1

TOTP (Time-based One-Time Password) ist in RFC 6238 spezifiziert und baut auf dem älteren HOTP-Algorithmus aus RFC 4226 auf. Statt eines monoton steigenden Zählers wie bei HOTP verwendet TOTP die aktuelle Unix-Zeit, geteilt durch ein festes Zeitfenster von meist 30 Sekunden: T = floor(unix_time / 30). Dieser Zähler T ersetzt den Counter-Wert im HOTP-Algorithmus.

Server und Client teilen sich vorab ein zufälliges Shared Secret, üblicherweise 160 Bit lang und Base32-kodiert für die manuelle Eingabe. Aus Secret und Zeitfenster berechnet der Algorithmus einen HMAC-SHA1-Hash, aus dessen letzten vier Bit eine Dynamic-Truncation-Funktion vier Byte extrahiert, die anschließend modulo 10^6 auf einen 6-stelligen Code reduziert werden. Weil Server- und Client-Uhr nie exakt synchron laufen, akzeptieren Implementierungen üblicherweise ein Toleranzfenster von einem Zeitschritt in beide Richtungen. Das Shared Secret darf das gesicherte Backend niemals im Klartext verlassen und muss verschlüsselt gespeichert werden, da es der einzige Faktor ist, der einen Angreifer dauerhaft zur Code-Generierung befähigt.

3. TOTP mit PHP implementieren: otphp und QR-Code-Provisionierung

Für die praktische Umsetzung in PHP hat sich die Library spomky-labs/otphp etabliert, die RFC 6238 vollständig implementiert und über Composer installierbar ist: composer require spomky-labs/otphp. Die Klasse OTPHP\TOTP generiert beim Anlegen automatisch ein kryptographisch sicheres Secret, sofern keines übergeben wird, und stellt über getProvisioningUri() die otpauth://-URI bereit, die Authenticator-Apps wie Google Authenticator oder Authy per QR-Code einlesen.

Der QR-Code selbst wird serverseitig nicht von otphp erzeugt, sondern von einer separaten Library wie endroid/qr-code, die die Provisioning-URI als PNG oder SVG rendert. Wichtig: Die URI enthält das Klartext-Secret und darf nur einmalig während der Enrollment-Phase über eine authentifizierte, TLS-gesicherte Verbindung übertragen werden, niemals in Logs oder E-Mails. Bei der Verifikation im Login-Flow ruft man verify() mit einem Toleranzfenster auf, damit kleine Zeitabweichungen zwischen Server und Smartphone nicht zu false negatives führen.


<?php

declare(strict_types=1);

namespace Mironsoft\SecurityDemo\Service;

use OTPHP\TOTP;

/**
 * Handles TOTP secret generation, provisioning URI creation and code verification.
 */
final class TotpService
{
    /**
     * Generate a new TOTP secret and provisioning URI for QR-code enrollment.
     *
     * @param string $accountName Usually the user's email address.
     * @param string $issuer Application/brand name shown in the authenticator app.
     * @return array{secret: string, uri: string}
     */
    public function generateSecret(string $accountName, string $issuer = 'Mironsoft Admin'): array
    {
        $totp = TOTP::create(
            secret: null,   // let otphp generate a cryptographically secure Base32 secret
            period: 30,     // RFC 6238 default time-step in seconds
            digest: 'sha1', // RFC 6238 default HMAC algorithm
            digits: 6
        );
        $totp->setLabel($accountName);
        $totp->setIssuer($issuer);

        return [
            'secret' => $totp->getSecret(),
            'uri' => $totp->getProvisioningUri(), // otpauth://totp/... for QR-code rendering
        ];
    }

    /**
     * Verify a user-submitted 6-digit code against the stored shared secret.
     *
     * @param string $secret Base32-encoded shared secret from the database.
     * @param string $code User-submitted one-time code.
     * @return bool True if the code is valid within the allowed clock-drift window.
     */
    public function verify(string $secret, string $code): bool
    {
        $totp = TOTP::create($secret, period: 30, digest: 'sha1', digits: 6);

        // Window of 1 tolerates +/-30s clock drift between server and device
        return $totp->verify($code, time(), 1);
    }
}

4. Backup-Codes: Generierung, Hashing und Single-Use-Invalidierung

Wer sein Smartphone verliert oder die Authenticator-App neu installiert, braucht einen Wiederherstellungsweg, der nicht vom zweiten Faktor selbst abhängt. Backup-Codes lösen dieses Henne-Ei-Problem: Bei der MFA-Aktivierung generiert das Backend eine feste Anzahl, üblicherweise 8 bis 10, zufälliger Einmal-Codes mit ausreichender Entropie, erzeugt über random_bytes() statt der unsicheren rand()-Familie.

Die Codes werden dem Nutzer genau einmal im Klartext angezeigt, etwa zum Ausdrucken oder Speichern in einem Passwort-Manager, und ausschließlich als Hash in der Datenbank gespeichert, idealerweise mit password_hash() und dem Algorithmus PASSWORD_ARGON2ID. Nach erfolgreicher Nutzung eines Codes wird der zugehörige Datensatz sofort als verbraucht markiert, etwa über ein used_at-Zeitstempelfeld, damit derselbe Code nicht ein zweites Mal funktioniert. Diese Single-Use-Invalidierung muss innerhalb derselben Datenbank-Transaktion wie die Login-Freigabe erfolgen, um Race Conditions bei parallelen Anfragen auszuschließen. Nach Verbrauch aller Codes sollte das System proaktiv zur Neugenerierung auffordern.


<?php

declare(strict_types=1);

namespace Mironsoft\SecurityDemo\Service;

/**
 * Generates and validates single-use MFA backup codes.
 */
final class BackupCodeService
{
    private const int CODE_COUNT = 10;
    private const int CODE_LENGTH = 10;

    /**
     * Generate a fresh batch of backup codes for a user.
     * Returns the plaintext codes once (shown to the user) plus their hashes for storage.
     *
     * @return array<int, array{plain: string, hash: string}>
     */
    public function generate(): array
    {
        $codes = [];
        for ($i = 0; $i < self::CODE_COUNT; $i++) {
            // random_bytes is CSPRNG-backed; bin2hex avoids ambiguous characters
            $plain = bin2hex(random_bytes((int) (self::CODE_LENGTH / 2)));
            $codes[] = [
                'plain' => $plain,
                'hash' => password_hash($plain, PASSWORD_ARGON2ID),
            ];
        }

        return $codes;
    }

    /**
     * Verify a submitted backup code against stored hashes and invalidate it on success.
     *
     * @param string $submittedCode Plaintext code entered by the user.
     * @param array<int, array{id: int, hash: string, used_at: ?string}> $storedCodes
     * @return int|null ID of the matched, now-invalidated code, or null if no match.
     */
    public function verifyAndInvalidate(string $submittedCode, array $storedCodes): ?int
    {
        foreach ($storedCodes as $stored) {
            if ($stored['used_at'] !== null) {
                continue; // already consumed, single-use enforcement
            }
            if (password_verify($submittedCode, $stored['hash'])) {
                return $stored['id']; // caller sets used_at = NOW() in the same transaction
            }
        }

        return null;
    }
}

5. WebAuthn/FIDO2: die phishing-resistente Alternative zu TOTP

WebAuthn, standardisiert vom W3C und Teil des breiteren FIDO2-Stacks zusammen mit dem CTAP2-Protokoll, ersetzt geteilte Geheimnisse durch asymmetrische Kryptographie. Bei der Registrierung erzeugt der Authenticator ein Schlüsselpaar, behält den privaten Schlüssel sicher verwahrt im Gerät und übermittelt nur den öffentlichen Schlüssel an den Server. Beim Login signiert der Authenticator eine vom Server generierte Zufalls-Challenge mit dem privaten Schlüssel, der Server verifiziert die Signatur mit dem gespeicherten öffentlichen Schlüssel.

Der entscheidende Phishing-Schutz liegt in der Origin-Bindung: Der Browser bindet jede Signatur kryptographisch an die tatsächliche Domain (Relying Party ID), sodass ein Credential, das für mironsoft.de registriert wurde, auf einer Phishing-Domain wie mironsoft-de.example schlicht nicht funktioniert. TOTP-Codes hingegen können auf einer gefälschten Login-Seite in Echtzeit abgephisht und an den echten Server weitergeleitet werden. Man unterscheidet Plattform-Authenticator, fest im Gerät verbaut wie Touch ID oder Windows Hello, von Roaming-Authenticator, externe Hardware-Token wie YubiKeys, die per USB, NFC oder Bluetooth verbunden werden.

6. WebAuthn Registrierung und Login technisch umsetzen

Die Registrierung eines WebAuthn-Credentials startet im Browser mit navigator.credentials.create(), dem ein PublicKeyCredentialCreationOptions-Objekt übergeben wird. Zentrale Felder sind die serverseitig generierte, kryptographisch zufällige challenge, die rp.id als Relying-Party-Domain, eine eindeutige user.id sowie die Liste erlaubter Signaturalgorithmen in pubKeyCredParams, üblicherweise ES256 (Algorithmus-ID -7) und RS256 (-257). Über authenticatorSelection.userVerification lässt sich erzwingen, dass der Authenticator zusätzlich Biometrie oder eine PIN abfragt, nicht nur bloße Anwesenheit.

Serverseitig übernimmt in PHP die Library web-auth/webauthn-lib die vollständige Verifikation der vom Browser zurückgegebenen Attestation: Signatur-Prüfung, Challenge-Abgleich und Extraktion des öffentlichen Schlüssels. Gespeichert werden der öffentliche Schlüssel, die Credential-ID und ein Signature-Counter, der bei jedem Login mit dem vom Authenticator gemeldeten Wert verglichen wird, um geklonte Authenticator-Hardware zu erkennen. Ein Login-Request nutzt anschließend navigator.credentials.get() mit derselben Challenge-Logik.


{
  "rp": {
    "name": "Mironsoft Admin",
    "id": "mironsoft.de"
  },
  "user": {
    "id": "MTIzNDU2Nzg5MA",
    "name": "admin@mironsoft.de",
    "displayName": "Admin User"
  },
  "challenge": "Y2hhbGxlbmdlLWZyb20tc2VydmVyLWNzcHJuZw",
  "pubKeyCredParams": [
    { "type": "public-key", "alg": -7 },
    { "type": "public-key", "alg": -257 }
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "residentKey": "preferred",
    "userVerification": "required"
  },
  "attestation": "none",
  "timeout": 60000
}

7. Magento_TwoFactorAuth: Modul-Aufbau und Provider

Magento liefert seit Version 2.3.7 das Modul Magento_TwoFactorAuth nativ mit, in Magento 2.4.x ist es standardmäßig aktiviert und deckt ausschließlich den Adminhtml-Bereich ab, nicht das Storefront-Login. Das Modul definiert eine ProviderInterface, über die mehrere Provider parallel registriert sind: google (TOTP-basiert, kompatibel mit Google Authenticator und Authy), authy (SMS- und Push-basiert über die Twilio-Authy-API), duo_security (Push-basierte Verifikation über Duo) sowie webauthn, das seit Magento 2.4.4 das ältere U2F-Modul ablöst und volles FIDO2 inklusive Plattform-Authenticator unterstützt.

Die Provider-Auswahl und Enforcement-Logik liegt in vendor/magento/module-two-factor-auth/etc/di.xml sowie den zugehörigen Konsolen-Commands. Jeder Admin-Nutzer durchläuft beim ersten Login nach Aktivierung einen Enrollment-Flow, in dem er den erzwungenen Provider einrichten muss, bevor er das Backend betreten kann. Für produktive Shops ist die Kombination aus einem TOTP-Provider als Basis und WebAuthn als phishing-resistente Ergänzung die robusteste Konfiguration.


<!-- app/code/Mironsoft/SecuritySuite/etc/config.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <twofactorauth>
            <general>
                <!-- Providers enforced for every admin user, comma-separated -->
                <force_providers>google,webauthn</force_providers>
            </general>
            <google>
                <!-- Issuer label shown in the authenticator app -->
                <issuer>Mironsoft Admin</issuer>
            </google>
            <webauthn>
                <!-- Relying Party ID must match the admin domain -->
                <rp_id>mironsoft.de</rp_id>
            </webauthn>
        </twofactorauth>
    </default>
</config>

8. Admin-MFA erzwingen: Konfiguration und CLI

Die Konfiguration erfolgt unter Stores > Configuration > Security > 2FA im Adminbereich oder direkt per CLI über bin/magento config:set. Der zentrale Konfigurationspfad twofactorauth/general/force_providers nimmt eine kommaseparierte Liste erzwungener Provider entgegen; ist ein Nutzer für keinen der gelisteten Provider registriert, blockiert Magento den Login, bis das Enrollment abgeschlossen ist. Provider-spezifische Einstellungen wie der Issuer-Name für TOTP-Apps oder die Relying-Party-ID für WebAuthn liegen unter eigenen Konfigurationspfaden je Provider.

Weil force_providers global für alle Admin-Nutzer gilt, empfiehlt sich vor dem Rollout ein Test mit einem Zweitaccount, um ein versehentliches Aussperren aller Administratoren zu vermeiden. Für den Notfall bietet Magento das Kommando bin/magento admin:user:unlock sowie datenbankseitige Recovery über die Tabelle tfa_user_config, in der der Enrollment-Status je Nutzer und Provider gespeichert ist. Nach jeder Konfigurationsänderung ist bin/magento cache:flush erforderlich, damit die neuen Werte im Admin-Login-Flow greifen.


# Enable the built-in Two-Factor Auth module (ships since Magento 2.3.7)
bin/magento module:enable Magento_TwoFactorAuth
bin/magento setup:upgrade

# Force Google Authenticator (TOTP) and WebAuthn for every admin user
bin/magento config:set twofactorauth/general/force_providers google,webauthn

# Set the issuer label shown inside the authenticator app
bin/magento config:set twofactorauth/google/issuer "Mironsoft Admin"

# Restrict the WebAuthn Relying Party ID to the admin domain
bin/magento config:set twofactorauth/webauthn/rp_id mironsoft.de

bin/magento cache:flush

9. MFA-Methoden im direkten Vergleich

SMS-basierte Einmalcodes, TOTP-Authenticator-Apps und WebAuthn/FIDO2 unterscheiden sich erheblich in Sicherheitsniveau, Implementierungsaufwand und Nutzererfahrung. Die folgende Übersicht fasst die wichtigsten Entscheidungskriterien für den Einsatz im Magento-Adminbereich zusammen.

Kriterium SMS-OTP TOTP (Authenticator-App) WebAuthn/FIDO2
Phishing-Resistenz Kein Schutz (abfangbar) Kein Schutz (Code abphishbar) Vollständig, dank Origin-Bindung
SIM-Swapping-Risiko Hoch Kein Risiko Kein Risiko
Laufende Kosten SMS-Gateway-Gebühren pro Login Keine laufenden Kosten Hardware-Key optional (10-50 EUR)
Offline-Fähigkeit Erfordert Mobilfunknetz Vollständig offline Vollständig offline
Implementierungsaufwand Gering (SMS-API) Mittel (Library + QR-Code) Hoch (Client- und Server-Logik)
Nutzererfahrung Hoch (bekannt) Mittel (App nötig) Sehr hoch (Biometrie, ein Tap)

In der Praxis ist die Kombination aus TOTP als kosteneffizientem Standardverfahren und WebAuthn als erzwungener Ergänzung für hochprivilegierte Accounts wie Administratoren die pragmatischste Lösung. SMS-OTP sollte in neuen Implementierungen grundsätzlich vermieden werden, da NIST SP 800-63B es bereits seit mehreren Jahren als eingeschränkt vertrauenswürdig einstuft und SIM-Swapping-Angriffe in der Praxis regelmäßig erfolgreich sind.

Mironsoft

Magento-Security-Audits, MFA-Implementierung und Admin-Härtung

Multi-Faktor-Authentifizierung professionell einführen?

Wir implementieren TOTP, Backup-Codes und WebAuthn/FIDO2 für euren Magento-Adminbereich, konfigurieren Magento_TwoFactorAuth korrekt und richten ein sicheres Recovery-Konzept für den Ernstfall ein.

MFA-Implementierung

TOTP, Backup-Codes und WebAuthn nach RFC 6238 und FIDO2-Standard

Magento-2FA-Konfiguration

Provider-Enforcement, Recovery-Prozesse und Admin-Härtung

Security-Audit

Vollständige Prüfung von Authentifizierung und Session-Handling

10. Zusammenfassung

Multi-Faktor-Authentifizierung technisch korrekt umzusetzen bedeutet, mehrere sich ergänzende Bausteine sauber zu kombinieren. TOTP nach RFC 6238 liefert mit einer Library wie spomky-labs/otphp einen kosteneffizienten Einstieg: Shared Secret generieren, per QR-Code provisionieren und Codes mit Toleranzfenster verifizieren. Backup-Codes schließen die Lücke bei Geräteverlust, müssen aber konsequent gehasht mit password_hash() und nach Nutzung sofort invalidiert werden, um nicht selbst zur Schwachstelle zu werden. WebAuthn/FIDO2 geht einen entscheidenden Schritt weiter und eliminiert das Grundproblem geteilter Geheimnisse durch asymmetrische Kryptographie und Origin-Bindung, wodurch Phishing-Angriffe auf den zweiten Faktor technisch wirkungslos werden.

Für Magento-Shops ist die native Integration über Magento_TwoFactorAuth der pragmatischste Startpunkt: Das Modul ist seit 2.3.7 vorhanden, unterstützt TOTP, WebAuthn und weitere Provider parallel und lässt sich über force_providers für alle Admin-Nutzer erzwingen. Wer diese Bausteine konsequent kombiniert, TOTP als Basis, WebAuthn als phishing-resistente Ergänzung für kritische Accounts, und Backup-Codes als kontrollierten Wiederherstellungsweg, reduziert das Risiko eines kompromittierten Admin-Zugangs drastisch, ohne die Nutzererfahrung unnötig zu verkomplizieren.

Multi-Faktor-Authentifizierung technisch umsetzen - Das Wichtigste auf einen Blick

TOTP nach RFC 6238

HMAC-SHA1 über ein 30-Sekunden-Zeitfenster, mit spomky-labs/otphp implementiert und per QR-Code provisioniert.

Backup-Codes

Mit random_bytes() generiert, per password_hash(ARGON2ID) gespeichert, nach Nutzung sofort invalidiert.

WebAuthn/FIDO2

Asymmetrische Kryptographie mit Origin-Bindung macht Phishing-Angriffe auf den zweiten Faktor wirkungslos.

Magento_TwoFactorAuth

Nativ seit 2.3.7, force_providers erzwingt TOTP und WebAuthn für alle Admin-Nutzer.

11. FAQ: Multi-Faktor-Authentifizierung technisch umsetzen

1Was ist der Unterschied zwischen TOTP und HOTP?
HOTP nutzt einen steigenden Zähler, TOTP ersetzt diesen durch die aktuelle Unix-Zeit geteilt durch ein festes Zeitfenster, meist 30 Sekunden. Der Code wechselt so automatisch ohne Synchronisationsaufwand.
2Wie sicher muss das TOTP-Shared-Secret gespeichert werden?
Verschlüsselt in der Datenbank, niemals im Klartext oder in Logs. Ein Leak entspricht dem dauerhaften Verlust des zweiten Faktors für alle betroffenen Konten.
3Welche PHP-Library eignet sich für TOTP?
spomky-labs/otphp implementiert RFC 6238 vollständig, inklusive Provisioning-URI und verify() mit Toleranzfenster für Zeitabweichungen.
4Wie sollten Backup-Codes gespeichert werden?
Nur einmal im Klartext angezeigt, danach ausschließlich als Hash mit password_hash(ARGON2ID) gespeichert. Nach Nutzung sofort als verbraucht markieren.
5Warum ist WebAuthn phishing-resistent, TOTP aber nicht?
WebAuthn bindet Signaturen kryptographisch an die Domain, ein Credential funktioniert auf Phishing-Domains nicht. TOTP-Codes sind reine Zahlen und können in Echtzeit weitergeleitet werden.
6Plattform- vs. Roaming-Authenticator?
Plattform-Authenticator sind fest im Gerät verbaut (Touch ID, Windows Hello). Roaming-Authenticator sind externe Hardware-Token wie YubiKeys per USB, NFC oder Bluetooth.
7Seit wann bringt Magento Two-Factor Auth mit?
Seit Magento 2.3.7 im Kern, in 2.4.x standardmäßig aktiv. Deckt nur den Adminhtml-Bereich ab, nicht das Storefront-Login.
8Wie erzwinge ich MFA für alle Admin-Nutzer?
Über twofactorauth/general/force_providers, per Adminbereich oder bin/magento config:set. Nutzer ohne registrierten Provider werden zum Enrollment gezwungen.
9Was passiert, wenn ein Admin sein Gerät verliert?
Backup-Codes ermöglichen weiterhin den Login. Ohne diese hilft die Recovery über tfa_user_config oder bin/magento admin:user:unlock durch einen anderen Administrator.
10Sollte man SMS-OTP noch einsetzen?
Für neue Implementierungen nicht empfohlen. NIST SP 800-63B stuft SMS-OTP als eingeschränkt vertrauenswürdig ein, SIM-Swapping-Angriffe sind in der Praxis regelmäßig erfolgreich.