Google身份验证器可用作公共服务吗?

ohh*_*hho 146 security authentication google-oauth

是否存在在自运行(例如LAMP堆栈)Web应用程序上使用Google身份验证器(双因素身份验证)的公共API ?

Wil*_*ung 120

项目是开源的.我没用过它.但是它使用了一个记录的算法(在开源项目页面上列出的RFC中注明),并且验证器实现支持多个帐户.

实际过程很简单.一次性代码本质上是伪随机数发生器.随机数生成器是一旦给出种子或起始编号,继续创建随机数流的公式.给定种子,虽然数字可能是彼此随机的,但序列本身是确定性的.因此,一旦您的设备和服务器"同步",那么每次点击"下一个数字按钮"时,设备创建的随机数将是服务器所期望的相同的随机数字.

安全的一次性密码系统比随机数生成器更复杂,但概念类似.还有其他细节可帮助保持设备和服务器同步.

因此,没有其他人可以托管身份验证,例如OAuth.相反,您需要实现与Google为移动设备提供的应用兼容的算法.该软件(应该)可用于开源项目.

根据您的复杂程度,您应该拥有实现此过程的服务器端所需的全部内容,以提供OSS项目和RFC.我不知道您的服务器软件是否有特定的实现(PHP,Java,.NET等)

但是,具体而言,您不需要异地服务来处理此问题.

  • 你的意思是短信?它很慢,不可靠和昂贵. (26认同)
  • 另一方面,在许多不同的移动设备上使用现有的,众所周知的,易于获得的解决方案是非常有益的...(提示提示) (3认同)
  • 自2016年8月起,FYI NIST不再建议使用SMS进行两因素身份验证。不要担心其不安全的成本。 (2认同)

rus*_*sau 56

该算法记录在RFC6238中.有点像这样:

  • 您的服务器为用户提供安装到Google身份验证器的秘密.Google将此作为此处记录的QR码执行此操作.
  • Google身份验证器通过Unix时间的SHA1-HMAC和秘密生成一个6位数的代码(在RFC中有更详细的内容)
  • 服务器还知道验证6位代码的秘密/ unix时间.

我在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/

在实现双因素身份验证时应该小心,您需要确保服务器和客户端上的时钟是同步的,以防止对令牌的暴力攻击并且使用的初始种子适当大.


Tad*_*eck 9

您可以使用我的解决方案,作为我的问题的答案(有完整的Python代码解释):

Python中的Google身份验证器实现

我认为用PHP或Perl实现它相当容易.如果您有任何问题,请告诉我.

我还将我的代码作为Python模块发布在GitHub上.


use*_*983 6

我发现了这个:https://github.com/PHPGangsta/GoogleAuthenticator.我测试了它,对我来说很好.


Ogg*_*las 5

不是 LAMP,但如果您使用 C#,这是我使用的代码:

代码最初来自:

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

Base32Encoding 类来自这个答案:

/sf/answers/499450591/

示例程序:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

总价:

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

}
Run Code Online (Sandbox Code Playgroud)

Base32编码:

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

}
Run Code Online (Sandbox Code Playgroud)