Google Authenticator tersedia sebagai layanan publik?

Jawaban:

121

The proyek open source. Saya belum menggunakannya. Tapi itu menggunakan algoritma yang didokumentasikan (dicatat dalam RFC yang tercantum pada halaman proyek sumber terbuka), dan implementasi authenticator mendukung banyak akun.

Proses yang sebenarnya mudah. Kode satu waktu, pada dasarnya, adalah generator angka acak semu. Penghasil bilangan acak adalah rumus yang pernah diberi seed, atau nomor awal, terus membuat aliran bilangan acak. Diberikan seed, sementara angka mungkin acak satu sama lain, urutannya sendiri bersifat deterministik. Jadi, setelah Anda memiliki perangkat dan server "dalam sinkronisasi" maka angka acak yang dibuat perangkat, setiap kali Anda menekan "tombol angka berikutnya", akan sama, acak, angka yang diharapkan server.

Sistem kata sandi satu kali yang aman lebih canggih daripada generator angka acak, tetapi konsepnya serupa. Ada juga detail lain untuk membantu menjaga perangkat dan server tetap sinkron.

Jadi, tidak perlu orang lain meng-host otentikasi, seperti, katakanlah OAuth. Alih-alih, Anda perlu menerapkan algoritma yang kompatibel dengan aplikasi yang disediakan Google untuk perangkat seluler. Perangkat lunak itu (harus) tersedia di proyek sumber terbuka.

Tergantung pada kecanggihan Anda, Anda harus memiliki semua yang Anda butuhkan untuk mengimplementasikan sisi server dari proses ini memberikan proyek OSS dan RFC. Saya tidak tahu apakah ada implementasi khusus untuk perangkat lunak server Anda (PHP, Java, .NET, dll.)

Tetapi, khususnya, Anda tidak perlu layanan luar kantor untuk menangani ini.

Will Hartung
sumber
3
di sisi lain, menggunakan solusi yang sudah ada, terkenal, mudah didapat tersedia di banyak perangkat seluler yang berbeda adalah manfaat besar ... (hint hint)
simpleuser
26
Maksudmu SMS? Itu lambat, tidak bisa diandalkan dan mahal.
Achraf Almouloudi
Saya membuat blog tentang cara menerapkan 2fa yang kompatibel dengan Google Authenticator / RFC6238 untuk situs web di java murni: asaph.org/2016/04/google-authenticator-2fa-java.html (steker tak tahu malu)
Asaph
2
FYI NIST Tidak Lagi Merekomendasikan Otentikasi Dua-Faktor Menggunakan SMS pada Agustus 2016. Jangankan biaya yang dianggap tidak aman.
TheDPQ
57

Algoritma ini didokumentasikan dalam RFC6238 . Agak seperti ini:

  • server Anda memberikan rahasia kepada pengguna untuk dipasang ke Google Authenticator. Google melakukan ini sebagai kode QR yang didokumentasikan di sini .
  • Google Authenticator menghasilkan kode 6 digit dari SHA1-HMAC dari waktu Unix dan rahasia (banyak detail lebih lanjut tentang ini di RFC)
  • Server juga mengetahui waktu rahasia / unix untuk memverifikasi kode 6 digit.

Saya sudah bermain menerapkan algoritma dalam javascript di sini: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/

russau
sumber
20

Ada berbagai perpustakaan untuk PHP (The LAMP Stack)

PHP

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

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

Anda harus berhati-hati ketika menerapkan dua faktor auth, Anda perlu memastikan jam Anda di server dan klien disinkronkan, bahwa ada perlindungan terhadap serangan brute force pada token dan bahwa benih awal yang digunakan cukup besar.

James
sumber
Kontennya bagus, tetapi siapa pun yang menggunakan tautan pertama harus menerapkan metode pencegahan injeksi SQL, karena ada beberapa kelemahan potensial. Lihatlah masalah yang diangkat untuk yang pertama. Tautan kedua sempurna.
Septronic
9

Anda dapat menggunakan solusi saya , diposting sebagai jawaban untuk pertanyaan saya (ada kode Python lengkap dan penjelasan ):

Implementasi Google Authenticator di Python

Agak mudah untuk mengimplementasikannya dalam PHP atau Perl, saya pikir. Jika Anda memiliki masalah dengan ini, beri tahu saya.

Saya juga memposting kode saya di GitHub sebagai modul Python.

Tadeck
sumber
1
Sedikit setelah fakta ... Saya hanya ingin menindaklanjuti dengan menyebutkan bahwa ada modul Perl di CPAN: Auth :: GoogleAuthenticator ( search.cpan.org/dist/Auth-GoogleAuthenticator ).
DavidO
3

Ya, tidak memerlukan layanan jaringan, karena aplikasi Google Authenticator tidak akan berkomunikasi dengan server google, hanya disinkronkan dengan rahasia initital yang dihasilkan oleh server Anda (input ke telepon Anda dari kode QR) sementara waktu berlalu.

diyism
sumber
2

Bukan LAMP tetapi jika Anda menggunakan C # ini adalah kode yang saya gunakan:

Kode berasal dari:

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

Kelas Base32Encoding dari jawaban ini:

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

Contoh 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");
    }

}
Ogglas
sumber
-1

Untuk pengguna C #, jalankan Aplikasi Konsol sederhana ini untuk memahami cara memverifikasi kode token satu kali. Perhatikan bahwa kita perlu menginstal pustaka Otp.Net dari paket Nuget terlebih dahulu.

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!");
            }
        }
}
Minh Nguyen
sumber