Google Authenticator bir kamu hizmeti olarak kullanılabilir mi?


Yanıtlar:


120

Proje açık kaynak. Ben kullanmadım. Ancak, belgelenmiş bir algoritma kullanıyor (açık kaynak proje sayfasında listelenen RFC'de belirtilmiştir) ve kimlik doğrulayıcı uygulamaları birden çok hesabı destekler.

Gerçek süreç basittir. Tek seferlik kod esasen sözde rasgele sayı üretecidir. Rastgele sayı üreteci, bir tohum veya başlangıç ​​numarası verildiğinde rastgele sayılar akışı oluşturmaya devam eden bir formüldür. Bir tohum verildiğinde, sayılar birbirine rasgele olabilirken, dizinin kendisi deterministiktir. Dolayısıyla, cihazınızı ve sunucunuzu "senkronize" ettikten sonra, "sonraki sayı düğmesine" her bastığınızda, cihazın oluşturduğu rastgele sayılar, sunucunun beklediği rasgele sayılarla aynı olacaktır.

Tek seferlik güvenli bir şifre sistemi, rastgele sayı üretecinden daha karmaşıktır, ancak konsept benzerdir. Cihazı ve sunucuyu senkronize tutmaya yardımcı olacak başka ayrıntılar da vardır.

Dolayısıyla, OAuth gibi kimlik doğrulamayı başka birinin barındırmasına gerek yoktur. Bunun yerine, Google'ın mobil cihazlar için sağladığı uygulamalarla uyumlu olan bu algoritmayı uygulamanız gerekir. Bu yazılım, açık kaynak projesinde mevcut (olmalıdır).

Kapsamlılığınıza bağlı olarak, bu işlemin sunucu tarafını uygulamak için ihtiyacınız olan her şeye OSS projesini ve RFC'yi vermelisiniz. Sunucu yazılımınız için özel bir uygulama olup olmadığını bilmiyorum (PHP, Java, .NET, vb.)

Ancak, özellikle bununla başa çıkmak için tesis dışında bir hizmete ihtiyacınız yoktur.


3
Öte yandan, birçok farklı mobil cihazda mevcut olan, iyi bilinen, elde edilmesi kolay bir çözümü kullanmak büyük fayda sağlar ... (ipucu ipucu)
simpleuser

26
SMS mi demek istiyorsun? Yavaş, güvenilmez ve maliyetlidir.
Achraf Almouloudi

Saf javada web siteleri için Google Authenticator / RFC6238 uyumlu 2fa'nın nasıl uygulanacağı hakkında blog yazdım: asaph.org/2016/04/google-authenticator-2fa-java.html (utanmaz eklenti)
Asaph

2
Bilginize, Ağustos 2016'dan İtibaren Artık SMS Kullanarak İki Faktörlü Kimlik Doğrulamayı Öneren NIST Artık Güvensiz olarak değerlendirilen maliyeti boşverin.
TheDPQ

57

Algoritma, RFC6238'de belgelenmiştir . Biraz şuna benzer:

  • sunucunuz, kullanıcıya Google Authenticator'a yüklemesi için bir sır verir. Google bunu burada belgelenen bir QR kodu olarak yapar .
  • Google Authenticator, Unix zamanının SHA1-HMAC'sinden ve sırrından 6 basamaklı bir kod oluşturur (RFC'de bununla ilgili çok daha fazla ayrıntı)
  • Sunucu ayrıca 6 basamaklı kodu doğrulamak için gizli / unix zamanını da bilir.

Burada javascript algoritmasını uygulayan bir oyun yaşadım: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/


20

PHP için çeşitli kitaplıklar vardır (LAMP Yığını)

PHP

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

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

İki faktörlü kimlik doğrulamayı uygularken dikkatli olmalısınız, sunucu ve istemcideki saatlerinizi senkronize etmelisiniz, belirteçte kaba kuvvet saldırılarına karşı koruma bulunduğundan ve kullanılan ilk tohumun uygun büyüklükte olduğundan emin olmalısınız.


İçerik harikaydı, ancak ilk bağlantıyı kullanan herkes, bazı olası kusurlar olduğu için SQL enjeksiyon önleme yöntemlerini uygulamalıdır. İlki için ortaya çıkan sorunlara bakın. İkinci bağlantı mükemmel.
Septronic

9

Sen edebilirsiniz benim çözüm kullanmak , soruma cevap olarak yayınlanmıştır (orada tam Python kodu ve açıklama ):

Python'da Google Authenticator uygulaması

PHP veya Perl'de uygulamak oldukça kolaydır, bence. Bununla ilgili herhangi bir sorununuz olursa lütfen bize bildirin.

Kodumu GitHub'a Python modülü olarak da gönderdim .


1
Küçük bir işten geçtikten sonra ... Sadece CPAN bir Perl modülü olduğunu belirterek yanıtlamak istedik: Auth :: GoogleAuthenticator ( search.cpan.org/dist/Auth-GoogleAuthenticator ).
DavidO


4

LAMP değil, ancak C # kullanıyorsanız, kullandığım kod budur:

Orijinal kod:

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

Base32Encoding sınıfı bu cevaptan geliyor:

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

Örnek program:

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

Evet, ağ hizmetine gerek yok, çünkü Google Authenticator uygulaması google sunucusuyla iletişim kurmayacak, yalnızca zaman geçerken sunucunuzun oluşturduğu (QR kodundan telefonunuza giriş) ilk sır ile senkronize olmaya devam ediyor.



-1

C # kullanıcısı için, bir defalık jeton kodunun nasıl doğrulanacağını anlamak için bu basit Konsol Uygulamasını çalıştırın. Öncelikle Nuget paketinden kitaplık Otp.Net'i kurmamız gerektiğini unutmayın .

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!");
            }
        }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.