Google Authenticator доступний як державна послуга?


Відповіді:


121

Проект з відкритим вихідним кодом. Я цим не користувався. Але він використовує задокументований алгоритм (зазначений у RFC, переліченому на сторінці проекту з відкритим кодом), а реалізатори автентифікатора підтримують кілька облікових записів.

Фактичний процес простий. Код одного разу, по суті, є генератором псевдовипадкових чисел. Генератор випадкових чисел - це формула, яка одного разу дала насіння, або початкове число, продовжує створювати потік випадкових чисел. З огляду на насіння, хоча числа можуть бути випадковими один до одного, сама послідовність є детермінованою. Отже, коли у вас пристрій і сервер "синхронізовані", то випадкові числа, які пристрій створює, щоразу, коли ви натискаєте "кнопку наступного числа", будуть однаковими, випадковими, числами, які очікує сервер.

Безпечна система одноразових паролів є більш досконалою, ніж генератор випадкових чисел, але концепція схожа. Також є інші деталі, які допомагають синхронізувати пристрій та сервер.

Отже, не потрібно, щоб хтось інший розміщував аутентифікацію, як, скажімо, OAuth. Натомість вам потрібно реалізувати алгоритм, сумісний із програмами, які Google надає для мобільних пристроїв. Це програмне забезпечення (повинно бути) доступне у проекті з відкритим кодом.

Залежно від вашої витонченості, ви повинні мати все необхідне для реалізації серверного боку цього процесу, надаючи проект OSS та RFC. Я не знаю, чи існує конкретна реалізація для вашого серверного програмного забезпечення (PHP, Java, .NET тощо)

Але, конкретно, вам не потрібна послуга з аутсайту для вирішення цього питання.


3
з іншого боку, використання вже існуючого, добре відомого, простого рішення, доступного для багатьох мобільних пристроїв, має велику користь ... (натяк натяк)
simpleuser

26
Ви маєте на увазі SMS? Це повільно, ненадійно і дорого.
Ахраф Алмулуді,

Я розмовляв про те, як реалізувати сумісний 2fa Google Authenticator / RFC6238 для веб-сайтів у чистому стилі Java: asaph.org/2016/04/google-authenticator-2fa-java.html (безсоромний модуль)
Asaph

2
FYI NIST більше не рекомендує двофакторну автентифікацію за допомогою SMS станом на серпень 2016 року. Не забувайте про вартість, яку вважають небезпечною.
TheDPQ

57

Алгоритм задокументований в RFC6238 . Виходить трохи так:

  • Ваш сервер дає користувачеві секрет для встановлення в Google Authenticator. Google робить це як QR-код, задокументований тут .
  • Google Authenticator генерує шестизначний код від SHA1-HMAC Unix часу та секрету (детальніше про це детальніше в RFC)
  • Сервер також знає тайну / unix час для перевірки 6-значного коду.

У мене була гра, що реалізує алгоритм у javascript тут: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/


20

Існує різноманітна бібліотека для PHP (стек LAMP)

PHP

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

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

Ви повинні бути обережними при впровадженні двофакторної автентичності. Вам потрібно забезпечити синхронізацію годинника на сервері та клієнті, що існує захист від жорстоких атак на маркер і що початкове використовуване насіння є достатньо великим.


Зміст був чудовим, але кожен, хто використовує перше посилання, повинен реалізувати методи запобігання ін'єкцій SQL, оскільки є деякі потенційні недоліки. Подивіться на питання, порушені для першого. Друге посилання ідеально.
Септронік

9

Ви можете використовувати моє рішення , розміщене як відповідь на моє запитання (є повний код Python та пояснення ):

Реалізація Google Authenticator в Python

Думаю, досить легко реалізувати це в PHP чи Perl. Якщо у вас є якісь проблеми з цим, будь ласка, повідомте мене про це.

Я також розмістив свій код на GitHub як модуль Python.


1
Трохи після факту ... Мені просто хотілося продовжити, зазначивши, що на CPAN є модуль Perl: Auth :: GoogleAuthenticator ( search.cpan.org/dist/Auth-GoogleAuthenticator ).
DavidO

6

Я знайшов це: https://github.com/PHPGangsta/GoogleAuthenticator . Я перевірив це і працює для мене чудово.


1
Я думаю, ви б віддали перевагу github.com/RobThree/TwoFactorAuth . Він базується на вищезгаданій бібліотеці, але це величезне вдосконалення з використанням більшої кількості функцій та чіткішої документації.
Ema4rl


3

Так, не потрібна послуга мережі, оскільки додаток Google Authenticator не спілкуватиметься з google-сервером, він просто синхронізується з початковою таємницею, яку ваш сервер генерує (вводить у ваш телефон із QR-коду) протягом часу.


2

Не LAMP, але якщо ви використовуєте C #, це код, який я використовую:

Код спочатку:

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

Клас Base32Encoding з цієї відповіді:

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

Приклад програми:

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();
    }
}

Топ:

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

}


-1

Для користувача C # запустіть це просте додаток консолі, щоб зрозуміти, як перевірити одноразовий маркерний код. Зауважте, що нам потрібно спочатку встановити бібліотеку Otp.Net з пакету Nuget.

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!");
            }
        }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.