Existe-t-il une API publique pour utiliser Google Authenticator ( authentification à deux facteurs) sur des applications Web auto-exécutables (par exemple, pile LAMP)?
Existe-t-il une API publique pour utiliser Google Authenticator ( authentification à deux facteurs) sur des applications Web auto-exécutables (par exemple, pile LAMP)?
Réponses:
Le projet est open source. Je ne l'ai pas utilisé. Mais il utilise un algorithme documenté (noté dans le RFC répertorié sur la page du projet open source), et les implémentations d'authentificateur prennent en charge plusieurs comptes.
Le processus réel est simple. Le code temporel unique est, essentiellement, un générateur de nombres pseudo aléatoires. Un générateur de nombres aléatoires est une formule qui, une fois donnée une graine, ou un nombre de départ, continue de créer un flux de nombres aléatoires. Étant donné une graine, alors que les nombres peuvent être aléatoires les uns par rapport aux autres, la séquence elle-même est déterministe. Ainsi, une fois que votre appareil et le serveur sont "synchronisés", les nombres aléatoires créés par l'appareil, chaque fois que vous appuyez sur le bouton "numéro suivant", seront les mêmes, aléatoires, que le serveur attend.
Un système de mot de passe à usage unique sécurisé est plus sophistiqué qu'un générateur de nombres aléatoires, mais le concept est similaire. Il existe également d'autres détails pour vous aider à synchroniser l'appareil et le serveur.
Il n'est donc pas nécessaire que quelqu'un d'autre héberge l'authentification, par exemple OAuth. Au lieu de cela, vous devez mettre en œuvre cet algorithme compatible avec les applications fournies par Google pour les appareils mobiles. Ce logiciel est (devrait être) disponible sur le projet open source.
En fonction de votre sophistication, vous devriez avoir tout ce dont vous avez besoin pour implémenter le côté serveur de ce processus, donner le projet OSS et le RFC. Je ne sais pas s'il existe une implémentation spécifique pour votre logiciel serveur (PHP, Java, .NET, etc.)
Mais, en particulier, vous n'avez pas besoin d'un service hors site pour gérer cela.
L'algorithme est documenté dans RFC6238 . Va un peu comme ça:
J'ai eu un jeu mettant en œuvre l'algorithme en javascript ici: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/
Il existe une variété de bibliothèques pour PHP (The LAMP Stack)
PHP
https://code.google.com/p/ga4php/
http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/
Vous devez être prudent lors de la mise en œuvre de l'authentification à deux facteurs, vous devez vous assurer que vos horloges sur le serveur et le client sont synchronisées, qu'une protection est en place contre les attaques par force brute sur le jeton et que la valeur de départ initiale utilisée est suffisamment grande.
Vous pouvez utiliser ma solution , publiée comme réponse à ma question (il y a le code Python complet et l' explication ):
Il est plutôt facile de l'implémenter en PHP ou Perl, je pense. Si vous rencontrez des problèmes, merci de me le faire savoir.
J'ai également publié mon code sur GitHub en tant que module Python.
J'ai trouvé ceci: https://github.com/PHPGangsta/GoogleAuthenticator . Je l'ai testé et fonctionne très bien pour moi.
Theres: https://www.gauthify.com qui le propose en tant que service
Pas LAMP mais si vous utilisez C # c'est le code que j'utilise:
Code originaire de:
https://github.com/kspearrin/Otp.NET
La classe Base32Encoding est issue de cette réponse:
https://stackoverflow.com/a/7135008/3850405
Exemple de programme:
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');
}
}
Encodage 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");
}
}
Pour ceux qui utilisent Laravel, ce https://github.com/sitepoint-editors/google-laravel-2FA est un bon moyen de résoudre ce problème.
Pour l'utilisateur C #, exécutez cette application console simple pour comprendre comment vérifier le code de jeton à usage unique. Notez que nous devons d'abord installer la bibliothèque Otp.Net à partir du package 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!");
}
}
}