자체 실행 (예 : LAMP 스택) 웹 앱 에서 Google OTP (2 단계 인증) 를 사용하기위한 공개 API가 있습니까?
자체 실행 (예 : LAMP 스택) 웹 앱 에서 Google OTP (2 단계 인증) 를 사용하기위한 공개 API가 있습니까?
답변:
프로젝트는 오픈 소스이다. 나는 그것을 사용하지 않았습니다. 그러나 문서화 된 알고리즘 (오픈 소스 프로젝트 페이지에 나열된 RFC에 명시되어 있음)을 사용하고 있으며 인증 자 구현은 여러 계정을 지원합니다.
실제 과정은 간단합니다. 일회성 코드는 본질적으로 의사 난수 생성기입니다. 난수 생성기는 일단 시드 또는 시작 번호가 주어지면 난수 스트림을 계속 생성하는 공식입니다. 시드가 주어지면 숫자는 서로 무작위 일 수 있지만 시퀀스 자체는 결정적입니다. 따라서 장치와 서버가 "동기화"되면 장치가 생성하는 난수는 "다음 숫자 버튼"을 누를 때마다 서버가 예상하는 것과 동일한 임의의 숫자입니다.
안전한 일회용 암호 시스템은 난수 생성기보다 정교하지만 개념은 유사합니다. 장치와 서버의 동기화를 유지하는 데 도움이되는 다른 세부 정보도 있습니다.
따라서 OAuth와 같이 다른 사람이 인증을 호스팅 할 필요가 없습니다. 대신 Google이 휴대 기기에 제공하는 앱과 호환되는 알고리즘을 구현해야합니다. 이 소프트웨어는 오픈 소스 프로젝트에서 사용 가능해야합니다.
정교함에 따라이 프로세스의 서버 측을 구현하는 데 필요한 모든 것이 OSS 프로젝트와 RFC를 제공해야합니다. 서버 소프트웨어 (PHP, Java, .NET 등)에 대한 특정 구현이 있는지 모르겠습니다.
그러나 특히이를 처리하기 위해 오프 사이트 서비스가 필요하지 않습니다.
알고리즘은 RFC6238에 문서화되어 있습니다 . 다음과 같이 조금갑니다 :
나는 자바 스크립트에서 알고리즘을 구현 한 플레이를 보았습니다 : http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/
PHP를위한 다양한 라이브러리 (LAMP 스택)가 있습니다
PHP
https://code.google.com/p/ga4php/
http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/
2 단계 인증을 구현할 때는 서버와 클라이언트의 시계가 동기화되고 토큰에 대한 무차별 대입 공격으로부터 보호되고 사용 된 초기 시드의 크기가 적절해야합니다.
내 질문에 대한 답변으로 게시 된 내 솔루션을 사용할 수 있습니다 ( 전체 Python 코드 및 설명이 있습니다 )
PHP 또는 Perl로 구현하는 것이 다소 쉽다고 생각합니다. 이것에 문제가 있으면 알려주십시오.
또한 GitHub 에 Python 모듈로 코드를 게시했습니다 .
나는 이것을 발견했다 : https://github.com/PHPGangsta/GoogleAuthenticator . 나는 그것을 테스트하고 나에게 잘 작동합니다.
서비스 : https://www.gauthify.com
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');
}
}
인코딩 :
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 # 사용자의 경우이 간단한 콘솔 앱을 실행하여 일회용 토큰 코드를 확인하는 방법을 이해하십시오. Nuget 패키지에서 라이브러리 Otp.Net 을 먼저 설치해야합니다 .
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!");
}
}
}