มี API สาธารณะสำหรับการใช้งานGoogle Authenticator (การตรวจสอบสิทธิ์แบบสองชั้น) ในแอปพลิเคชันเว็บที่ทำงานด้วยตนเอง (เช่น LAMP stack) หรือไม่
มี API สาธารณะสำหรับการใช้งานGoogle Authenticator (การตรวจสอบสิทธิ์แบบสองชั้น) ในแอปพลิเคชันเว็บที่ทำงานด้วยตนเอง (เช่น LAMP stack) หรือไม่
คำตอบ:
โครงการเปิดแหล่งที่มา ฉันไม่ได้ใช้มัน แต่มันใช้อัลกอริทึมที่ทำเป็นเอกสาร (ระบุไว้ใน RFC ที่แสดงรายการอยู่ในหน้าโครงการโอเพนซอร์ส) และการใช้งานตัวตรวจสอบความถูกต้องรองรับหลายบัญชี
กระบวนการจริงนั้นตรงไปตรงมา รหัสครั้งเดียวคือตัวสร้างตัวเลขสุ่มหลอก เครื่องกำเนิดตัวเลขสุ่มเป็นสูตรที่ครั้งหนึ่งได้รับเมล็ดหรือหมายเลขเริ่มต้นยังคงสร้างกระแสของตัวเลขสุ่ม ให้เมล็ดในขณะที่ตัวเลขอาจสุ่มกันลำดับนั้นเป็นแบบกำหนดขึ้น ดังนั้นเมื่อคุณมีอุปกรณ์และเซิร์ฟเวอร์ของคุณ "ซิงค์" แล้วตัวเลขสุ่มที่อุปกรณ์สร้างขึ้นทุกครั้งที่คุณกดปุ่ม "หมายเลขถัดไป" จะเป็นตัวเลขสุ่มที่เซิร์ฟเวอร์คาดหวัง
ระบบรหัสผ่านครั้งเดียวที่ปลอดภัยนั้นซับซ้อนกว่าตัวสร้างตัวเลขแบบสุ่ม แต่แนวคิดก็คล้ายกัน นอกจากนี้ยังมีรายละเอียดอื่น ๆ เพื่อช่วยให้อุปกรณ์และเซิร์ฟเวอร์ซิงค์กัน
ดังนั้นไม่จำเป็นต้องให้บุคคลอื่นเป็นเจ้าภาพในการตรวจสอบสิทธิ์เช่นพูด OAuth แต่คุณต้องใช้อัลกอริทึมนั้นที่เข้ากันได้กับแอพที่ Google จัดให้สำหรับอุปกรณ์มือถือ ซอฟต์แวร์ดังกล่าวมีอยู่ในโครงการโอเพ่นซอร์ส
ขึ้นอยู่กับความซับซ้อนของคุณคุณควรมีทุกสิ่งที่คุณต้องใช้ฝั่งเซิร์ฟเวอร์ของกระบวนการนี้ให้โครงการ OSS และ RFC ฉันไม่ทราบว่ามีการใช้งานเฉพาะสำหรับซอฟต์แวร์เซิร์ฟเวอร์ของคุณ (PHP, Java, .NET และอื่น ๆ )
แต่โดยเฉพาะคุณไม่ต้องการบริการนอกสถานที่เพื่อจัดการกับเรื่องนี้
อัลกอริทึมการบันทึกไว้ในRFC6238 ไปแบบนี้:
ฉันเคยเล่นการใช้อัลกอริทึมใน javascript ที่นี่: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/
มีไลบรารี่หลากหลายสำหรับ PHP (The LAMP Stack)
PHP
https://code.google.com/p/ga4php/
http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/
คุณควรระวังเมื่อใช้การตรวจสอบสองปัจจัยคุณต้องตรวจสอบให้แน่ใจว่านาฬิกาของคุณบนเซิร์ฟเวอร์และไคลเอนต์นั้นได้รับการซิงโครไนซ์ว่ามีการป้องกันการโจมตีด้วยโทเค็นแรง ๆ บนโทเค็น
คุณสามารถใช้วิธีการแก้ปัญหาของฉันโพสต์เป็นคำตอบสำหรับคำถามของฉัน (มีรหัสหลามเต็มและคำอธิบาย ):
ฉันคิดว่ามันค่อนข้างง่ายที่จะนำไปใช้ใน PHP หรือ Perl หากคุณมีปัญหาใด ๆ กับสิ่งนี้โปรดแจ้งให้เราทราบ
ฉันได้โพสต์รหัสของฉันบน GitHubเป็นโมดูล Python
Theres: https://www.gauthify.comที่ให้บริการเป็น
ใช่ไม่จำเป็นต้องใช้บริการเครือข่ายเนื่องจากแอป Google Authenticator จะไม่สื่อสารกับเซิร์ฟเวอร์ google เพียง แต่จะซิงค์กับความลับเริ่มต้นที่เซิร์ฟเวอร์ของคุณสร้างขึ้น (ใส่ลงในโทรศัพท์ของคุณจากรหัส QR) ในขณะที่เวลาผ่านไป
ไม่ใช่ 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();
}
}
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");
}
}
สำหรับผู้ที่ใช้ Laravel https://github.com/sitepoint-editors/google-laravel-2FAนี้เป็นวิธีที่ดีในการแก้ปัญหานี้
สำหรับผู้ใช้ 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!");
}
}
}