Có API công khai để sử dụng Google Authenticator ( xác thực hai yếu tố) trên các ứng dụng web tự chạy (ví dụ: LAMP stack) không?
Có API công khai để sử dụng Google Authenticator ( xác thực hai yếu tố) trên các ứng dụng web tự chạy (ví dụ: LAMP stack) không?
Câu trả lời:
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.
Thuật toán được ghi lại trong RFC6238 . Đi một chút như thế này:
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/
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.
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ã Python và giải thích ):
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.
Tôi đã tìm thấy cái này: https://github.com/PHPGangsta/GoogleAuthenticator . Tôi đã thử nghiệm nó và làm việc tốt cho tôi.
Có: https://www.gauthify.com cung cấp dịch vụ này dưới dạng dịch vụ
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");
}
}
Đối với những người sử dụng Laravel, https://github.com/sitepoint-editors/google-laravel-2FA này là một cách hay để giải quyết vấn đề này.
Đố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!");
}
}
}