¿Google Authenticator está disponible como servicio público?


¿Existe una API pública para usar el Google Authenticator (autenticación de dos factores) en aplicaciones web autoejecutables (por ejemplo, LAMP stack)?

Author: mate64, 2011-02-23

9 answers

El proyecto es de código abierto. No lo he usado. Pero está usando un algoritmo documentado (anotado en el RFC listado en la página del proyecto de código abierto), y las implementaciones de authenticator admiten varias cuentas.

El proceso real es sencillo. El código de tiempo es, esencialmente, un pseudo random number generator. Un generador de números aleatorios es una fórmula que una vez dada una semilla, o número inicial, continúa creando un flujo de números aleatorios. Dado una semilla, mientras los números pueden ser aleatorios entre sí, la secuencia en sí es determinista. Por lo tanto, una vez que tenga su dispositivo y el servidor "sincronizados", los números aleatorios que crea el dispositivo, cada vez que presione el botón "siguiente número", serán los mismos números aleatorios que el servidor espera.

Un sistema seguro de contraseñas de un solo uso es más sofisticado que un generador de números aleatorios, pero el concepto es similar. También hay otros detalles para ayudar a mantener el dispositivo y el servidor sincronizados.

So, no hay necesidad de que alguien más aloje la autenticación, como, por ejemplo, OAuth. En su lugar, debe implementar ese algoritmo que sea compatible con las aplicaciones que Google proporciona para los dispositivos móviles. Ese software está (debería estar) disponible en el proyecto de código abierto.

Dependiendo de su sofisticación, usted debe tener todo lo que necesita para implementar el lado del servidor de este proceso dar el proyecto OSS y el RFC. No sé si hay una implementación específica para su software de servidor (PHP, Java,. NET, etc.)

Pero, específicamente, no necesita un servicio externo para manejar esto.

 112
Author: Will Hartung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-23 04:44:11

El algoritmo está documentado en RFC6238. Va un poco como esto:

  • su servidor le da al usuario un secreto para instalar en Google Authenticator. Google hace esto como un código QR documentado aquí .
  • Google Authenticator genera un código de 6 dígitos a partir de un SHA1-HMAC del tiempo Unix y el secreto (mucho más detalle sobre esto en el RFC)
  • El servidor también conoce la hora secreta / unix para verificar el código de 6 dígitos.

He tenido una obra implementando el algoritmo en javascript aquí: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/

 47
Author: russau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-10 01:36:34

Hay una variedad de bibliotecas para PHP (La pila LAMP)

PHP

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

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

Debe tener cuidado al implementar autenticación de dos factores, debe asegurarse de que sus relojes en el servidor y el cliente estén sincronizados, que haya protección contra ataques de fuerza bruta en el token y que la semilla inicial utilizado es convenientemente grande.

 20
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-31 14:13:24

Puedes usar mi solución , publicada como respuesta a mi pregunta (hay código Python completo y explicación):

Implementación de Google Authenticator en Python

Creo que es bastante fácil implementarlo en PHP o Perl. Si tiene algún problema con esto, por favor hágamelo saber.

También he publicado mi código en GitHub como módulo Python.

 9
Author: Tadeck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:32

Encontré esto: https://github.com/PHPGangsta/GoogleAuthenticator. Lo probé y funciona bien para mí.

 6
Author: user1893983,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-29 13:12:20

Theres: https://www.gauthify.com que lo ofrece como un servicio

 4
Author: NoviceCoding,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-16 00:59:22

Sí, no necesita servicio de red, porque la aplicación Google Authenticator no se comunicará con el servidor de Google, solo se mantiene sincronizada con el secreto de inicio que genera su servidor(entrada en su teléfono desde el código QR) mientras pasa el tiempo.

 2
Author: diyism,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-17 16:59:38

No LAMP pero si usas C# este es el código que uso:

Código original de:

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

La clase Base32Encoding proviene de esta respuesta:

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

Programa de ejemplo:

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

}
 2
Author: Ogglas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-27 08:36:59

Para aquellos que usan Laravel, este https://github.com/sitepoint-editors/google-laravel-2FA es una buena manera de resolver este problema.

 1
Author: briankip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-12 16:15:34