it-swarm.com.de

Google Authenticator als öffentlicher Dienst verfügbar?

Gibt es eine öffentliche API für die Verwendung der Google Authenticator (Zwei-Faktor-Authentifizierung) auf selbst laufenden Web-Apps (z. B. LAMP-Stack)?

142
ohho

Das Projekt ist Open Source. Ich habe es nicht benutzt. Es wird jedoch ein dokumentierter Algorithmus verwendet (im RFC auf der Open-Source-Projektseite angegeben). Die Implementierungen des Authentifikators unterstützen mehrere Konten.

Der eigentliche Prozess ist unkompliziert. Der Einmalcode ist im Wesentlichen ein Pseudo-Zufallszahlengenerator. Ein Zufallszahlengenerator ist eine Formel, die, sobald ein Startwert oder eine Startnummer gegeben wurde, einen Strom von Zufallszahlen erstellt. In Anbetracht eines Startwertes ist die Sequenz selbst deterministisch, während die Zahlen zufällig zueinander sein können. Sobald Ihr Gerät und der Server "synchronisiert" sind, sind die Zufallszahlen, die das Gerät erstellt, jedes Mal, wenn Sie die Schaltfläche "nächste Zahl" drücken, dieselben, zufälligen Zahlen, die der Server erwartet.

Ein sicheres Einmalkennwortsystem ist komplexer als ein Zufallszahlengenerator, das Konzept ist jedoch ähnlich. Es gibt auch andere Details, um das Gerät und den Server synchron zu halten.

Es ist also nicht erforderlich, dass jemand anderes die Authentifizierung hostet, wie z. B. OAuth. Stattdessen müssen Sie diesen Algorithmus implementieren, der mit den Apps von Google für die mobilen Geräte kompatibel ist. Diese Software ist (sollte) im Open Source-Projekt verfügbar sein.

Abhängig von Ihrer Komplexität sollten Sie über alles verfügen, um die Serverseite dieses Prozesses zu implementieren, die das OSS-Projekt und den RFC enthält. Ich weiß nicht, ob es eine spezielle Implementierung für Ihre Serversoftware gibt (PHP, Java, .NET usw.).

Dafür benötigen Sie keinen Offsite-Service.

114
Will Hartung

Der Algorithmus ist in RFC6238 dokumentiert. Geht ein bisschen so:

  • ihr Server gibt dem Benutzer ein Geheimnis für die Installation in Google Authenticator. Google macht dies als QR-Code dokumentiert hier .
  • Google Authenticator generiert einen 6-stelligen Code, der aus einem SHA1-HMAC der Unix-Zeit und dem Geheimnis (mehr Details im RFC) stammt.
  • Der Server kennt auch die geheime/unix-Zeit, um den 6-stelligen Code zu überprüfen.

Ich habe ein Spiel dabei gehabt, den Algorithmus in Javascript zu implementieren: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/

51
russau

Es gibt verschiedene Bibliotheken für PHP (The LAMP Stack)

PHP

https://code.google.com/p/ga4php/

http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/

Sie sollten beim Implementieren der Zwei-Faktor-Authentifizierung vorsichtig sein. Sie müssen sicherstellen, dass Ihre Uhren auf dem Server und dem Client synchronisiert sind, dass ein Schutz vor Brute-Force-Angriffen auf das Token vorhanden ist und dass der verwendete Anfangswert ausreichend groß ist.

21
James

Sie können meine Lösung verwenden, die als Antwort auf meine Frage gepostet wird (es gibt vollständigen Python-Code und Erklärung):

Google Authenticator-Implementierung in Python

Es ist ziemlich einfach, es in PHP oder Perl zu implementieren, denke ich. Wenn Sie Probleme damit haben, lassen Sie es mich wissen.

Ich habe auch meinen Code auf GitHub als Python-Modul gepostet.

9
Tadeck

Ich habe folgendes gefunden: https://github.com/PHPGangsta/GoogleAuthenticator . Ich habe es getestet und funktioniert gut für mich.

7
user1893983

Theres: https://www.gauthify.com das bietet es als Service an

4
NoviceCoding

Nicht LAMP, aber wenn Sie C # verwenden, ist dies der Code, den ich verwende:

Code ursprünglich von:

https://github.com/kspearrin/Otp.NET

Die Base32Encoding-Klasse stammt aus dieser Antwort:

https://stackoverflow.com/a/7135008/3850405

Beispielprogramm:

class Program
{
    static void Main(string[] args)
    {
        var bytes = Base32Encoding.ToBytes("JBSWY3DPEHPK3PXP");

        var totp = new Totp(bytes);

        var result = totp.ComputeTotp();
        var remainingTime = totp.RemainingSeconds();
    }
}

Totp:

public class Totp
{
    const long unixEpochTicks = 621355968000000000L;

    const long ticksToSeconds = 10000000L;

    private const int step = 30;

    private const int totpSize = 6;

    private byte[] key;

    public Totp(byte[] secretKey)
    {
        key = secretKey;
    }

    public string ComputeTotp()
    {
        var window = CalculateTimeStepFromTimestamp(DateTime.UtcNow);

        var data = GetBigEndianBytes(window);

        var hmac = new HMACSHA1();
        hmac.Key = key;
        var hmacComputedHash = hmac.ComputeHash(data);

        int offset = hmacComputedHash[hmacComputedHash.Length - 1] & 0x0F;
        var otp = (hmacComputedHash[offset] & 0x7f) << 24
               | (hmacComputedHash[offset + 1] & 0xff) << 16
               | (hmacComputedHash[offset + 2] & 0xff) << 8
               | (hmacComputedHash[offset + 3] & 0xff) % 1000000;

        var result = Digits(otp, totpSize);

        return result;
    }

    public int RemainingSeconds()
    {
        return step - (int)(((DateTime.UtcNow.Ticks - unixEpochTicks) / ticksToSeconds) % step);
    }

    private byte[] GetBigEndianBytes(long input)
    {
        // Since .net uses little endian numbers, we need to reverse the byte order to get big endian.
        var data = BitConverter.GetBytes(input);
        Array.Reverse(data);
        return data;
    }

    private long CalculateTimeStepFromTimestamp(DateTime timestamp)
    {
        var unixTimestamp = (timestamp.Ticks - unixEpochTicks) / ticksToSeconds;
        var window = unixTimestamp / (long)step;
        return window;
    }

    private string Digits(long input, int digitCount)
    {
        var truncatedValue = ((int)input % (int)Math.Pow(10, digitCount));
        return truncatedValue.ToString().PadLeft(digitCount, '0');
    }

}

Base32Encoding:

public static class Base32Encoding
{
    public static byte[] ToBytes(string input)
    {
        if (string.IsNullOrEmpty(input))
        {
            throw new ArgumentNullException("input");
        }

        input = input.TrimEnd('='); //remove padding characters
        int byteCount = input.Length * 5 / 8; //this must be TRUNCATED
        byte[] returnArray = new byte[byteCount];

        byte curByte = 0, bitsRemaining = 8;
        int mask = 0, arrayIndex = 0;

        foreach (char c in input)
        {
            int cValue = CharToValue(c);

            if (bitsRemaining > 5)
            {
                mask = cValue << (bitsRemaining - 5);
                curByte = (byte)(curByte | mask);
                bitsRemaining -= 5;
            }
            else
            {
                mask = cValue >> (5 - bitsRemaining);
                curByte = (byte)(curByte | mask);
                returnArray[arrayIndex++] = curByte;
                curByte = (byte)(cValue << (3 + bitsRemaining));
                bitsRemaining += 3;
            }
        }

        //if we didn't end with a full byte
        if (arrayIndex != byteCount)
        {
            returnArray[arrayIndex] = curByte;
        }

        return returnArray;
    }

    public static string ToString(byte[] input)
    {
        if (input == null || input.Length == 0)
        {
            throw new ArgumentNullException("input");
        }

        int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
        char[] returnArray = new char[charCount];

        byte nextChar = 0, bitsRemaining = 5;
        int arrayIndex = 0;

        foreach (byte b in input)
        {
            nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
            returnArray[arrayIndex++] = ValueToChar(nextChar);

            if (bitsRemaining < 4)
            {
                nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
                returnArray[arrayIndex++] = ValueToChar(nextChar);
                bitsRemaining += 5;
            }

            bitsRemaining -= 3;
            nextChar = (byte)((b << bitsRemaining) & 31);
        }

        //if we didn't end with a full char
        if (arrayIndex != charCount)
        {
            returnArray[arrayIndex++] = ValueToChar(nextChar);
            while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
        }

        return new string(returnArray);
    }

    private static int CharToValue(char c)
    {
        int value = (int)c;

        //65-90 == uppercase letters
        if (value < 91 && value > 64)
        {
            return value - 65;
        }
        //50-55 == numbers 2-7
        if (value < 56 && value > 49)
        {
            return value - 24;
        }
        //97-122 == lowercase letters
        if (value < 123 && value > 96)
        {
            return value - 97;
        }

        throw new ArgumentException("Character is not a Base32 character.", "c");
    }

    private static char ValueToChar(byte b)
    {
        if (b < 26)
        {
            return (char)(b + 65);
        }

        if (b < 32)
        {
            return (char)(b + 24);
        }

        throw new ArgumentException("Byte is not a value Base32 value.", "b");
    }

}
3
Ogglas

Ja, Sie benötigen keinen Netzwerkdienst, da die Google Authenticator-App nicht mit dem Google-Server kommuniziert. Sie wird nur mit dem ursprünglichen Geheimnis synchronisiert, das Ihr Server generiert (Eingabe des QR-Codes in Ihr Telefon), während die Zeit verstrichen ist.

3
diyism

Für diejenigen, die Laravel verwenden, ist diese https://github.com/sitepoint-editors/google-laravel-2FA ein guter Weg, um dieses Problem zu lösen.

1
briankip

Führen Sie für C # -Benutzer diese einfache Konsolen-App aus, um zu erfahren, wie der Einmal-Tokencode überprüft wird. Beachten Sie, dass wir zuerst die Bibliothek Otp.Net aus dem Nuget-Paket installieren müssen.

static string secretKey = "JBSWY3DPEHPK3PXP"; //add this key to your Google Authenticator app  

private static void Main(string[] args)
{
        var bytes = Base32Encoding.ToBytes(secretKey);

        var totp = new Totp(bytes);

        while (true)
        {
            Console.Write("Enter your code from Google Authenticator app: ");
            string userCode = Console.ReadLine();

            //Generate one time token code
            string tokenInApp = totp.ComputeTotp();
            int remainingSeconds = totp.RemainingSeconds();

            if (userCode.Equals(tokenInApp)
                && remainingSeconds > 0)
            {
                Console.WriteLine("Success!");
            }
            else
            {
                Console.WriteLine("Failed. Try again!");
            }
        }
}
0
Minh Nguyen