Google Authenticator có sẵn dưới dạng dịch vụ công cộng?


Câu trả lời:


121

Các dự án là nguồn mở. Tôi đã không sử dụng nó. Nhưng đó là sử dụng thuật toán được ghi lại (được ghi chú trong RFC được liệt kê trên trang dự án nguồn mở) và các triển khai trình xác thực hỗ trợ nhiều tài khoản.

Quá trình thực tế là đơn giản. Mã một lần về cơ bản là một trình tạo số ngẫu nhiên giả. Trình tạo số ngẫu nhiên là một công thức mà một khi đã cho một hạt giống hoặc số bắt đầu, tiếp tục tạo ra một luồng các số ngẫu nhiên. Cho một hạt giống, trong khi các số có thể là ngẫu nhiên với nhau, trình tự chính nó là xác định. Vì vậy, một khi bạn có thiết bị của mình và máy chủ "đồng bộ hóa" thì các số ngẫu nhiên mà thiết bị tạo ra, mỗi lần bạn nhấn "nút số tiếp theo", sẽ giống nhau, ngẫu nhiên, số mà máy chủ mong đợi.

Một hệ thống mật khẩu một lần an toàn tinh vi hơn một trình tạo số ngẫu nhiên, nhưng khái niệm này tương tự nhau. Ngoài ra còn có các chi tiết khác để giúp giữ cho thiết bị và máy chủ đồng bộ.

Vì vậy, không cần người khác lưu trữ xác thực, như, nói OAuth. Thay vào đó, bạn cần triển khai thuật toán tương thích với các ứng dụng mà Google cung cấp cho các thiết bị di động. Phần mềm đó là (nên có) trên dự án nguồn mở.

Tùy thuộc vào sự tinh tế của bạn, bạn sẽ có tất cả những gì bạn cần để thực hiện phía máy chủ của quy trình này, cung cấp cho dự án OSS và RFC. Tôi không biết có triển khai cụ thể cho phần mềm máy chủ của bạn không (PHP, Java, .NET, v.v.)

Nhưng, cụ thể, bạn không cần một dịch vụ ngoại vi để xử lý việc này.


3
mặt khác, sử dụng một giải pháp đã có sẵn, nổi tiếng, dễ kiếm có sẵn trên nhiều thiết bị di động khác nhau là rất có lợi ... (gợi ý gợi ý)
đơn giản

26
Ý bạn là SMS? Nó là chậm, không đáng tin cậy và tốn kém.
Achraf Almouloudi

Tôi đã viết blog về cách triển khai 2fa tương thích Google Authenticator / RFC6238 cho các trang web trong java thuần túy: asaph.org/2016/04/google-authenticator-2fa-java.html (phích cắm không biết xấu hổ)
Asaph

2
FYI NIST sẽ không còn khuyến nghị Xác thực hai yếu tố sử dụng SMS vào tháng 8 năm 2016. Không bao giờ bỏ qua chi phí được coi là không an toàn.
TheDPQ

57

Thuật toán được ghi lại trong RFC6238 . Đi một chút như thế này:

  • máy chủ của bạn cung cấp cho người dùng một bí mật để cài đặt vào Google Authenticator. Google làm điều này như một mã QR được ghi lại ở đây .
  • Google Authenticator tạo mã gồm 6 chữ số từ SHA1-HMAC của thời Unix và bí mật (chi tiết hơn về điều này trong RFC)
  • Máy chủ cũng biết thời gian bí mật / unix để xác minh mã 6 chữ số.

Tôi đã có một trò chơi triển khai thuật toán trong javascript tại đây: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-alerskym-in-javascript/


20

Có nhiều thư viện cho PHP (LAMP Stack)

PHP

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

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

Bạn nên cẩn thận khi thực hiện xác thực hai yếu tố, bạn cần đảm bảo đồng hồ trên máy chủ và máy khách được đồng bộ hóa, có bảo vệ chống lại các cuộc tấn công vũ phu vào mã thông báo và hạt giống ban đầu được sử dụng là lớn phù hợp.


Nội dung rất hay, nhưng bất kỳ ai sử dụng liên kết đầu tiên nên thực hiện các phương pháp ngăn ngừa tiêm SQL, vì có một số lỗ hổng tiềm năng. Nhìn vào các vấn đề được nêu ra cho cái đầu tiên. Liên kết thứ hai là hoàn hảo.
Septronic

9

Bạn có thể sử dụng giải pháp của tôi , được đăng dưới dạng câu trả lời cho câu hỏi của tôi (có đầy đủ mã Pythongiải thích ):

Triển khai Google Authenticator bằng Python

Nó khá dễ dàng để thực hiện nó trong PHP hoặc Perl, tôi nghĩ vậy. Nếu bạn có bất kỳ vấn đề với điều này, xin vui lòng cho tôi biết.

Tôi cũng đã đăng mã của mình trên GitHub dưới dạng mô-đun Python.


1
Một chút sau khi thực tế ... Tôi chỉ muốn theo dõi bằng cách đề cập rằng có một mô-đun Perl trên CPAN: Auth :: GoogleAuthenticator ( search.cpan.org/dist/Auth-GoogleAuthenticator ).
DavidO



3

Có, không cần dịch vụ mạng, vì ứng dụng Google Authenticator sẽ không liên lạc với máy chủ google, nó chỉ được đồng bộ hóa với bí mật ban đầu mà máy chủ của bạn tạo ra (nhập vào điện thoại của bạn từ mã QR) trong khi thời gian trôi qua.


2

Không phải LAMP nhưng nếu bạn sử dụng C # thì đây là mã tôi sử dụng:

Mã ban đầu từ:

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

Lớp Base32Encoding là từ câu trả lời này:

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

Chương trình ví dụ:

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

}

Mã hóa cơ sở32:

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

Đối với người dùng C #, hãy chạy Ứng dụng Bảng điều khiển đơn giản này để hiểu cách xác minh mã mã thông báo một lần. Lưu ý rằng trước tiên chúng ta cần cài đặt thư viện Otp.Net từ gói 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!");
            }
        }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.