python security authentication one-time-password google-authenticator

Implementación de Google Authenticator en Python



security authentication (2)

Quería establecer una recompensa por mi pregunta, pero he logrado crear una solución. Mi problema parecía estar relacionado con el valor incorrecto de la clave secret (debe ser el parámetro correcto para la función base64.b32decode() ).

A continuación, publico una solución de trabajo completa con explicaciones sobre cómo usarla.

Código

El siguiente código es suficiente. También lo he subido a GitHub como un módulo separado llamado onetimepass (disponible aquí: https://github.com/tadeck/onetimepass ).

import hmac, base64, struct, hashlib, time def get_hotp_token(secret, intervals_no): key = base64.b32decode(secret, True) msg = struct.pack(">Q", intervals_no) h = hmac.new(key, msg, hashlib.sha1).digest() o = ord(h[19]) & 15 h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000 return h def get_totp_token(secret): return get_hotp_token(secret, intervals_no=int(time.time())//30)

Tiene dos funciones:

  • get_hotp_token() genera un token único (que debe invalidar después de un uso único),
  • get_totp_token() genera token en función del tiempo (cambiado en intervalos de 30 segundos),

Parámetros

Cuando se trata de parámetros:

  • secret es un valor secreto conocido por el servidor (la secuencia de comandos anterior) y el cliente (Google Authenticator, proporcionándolo como contraseña dentro de la aplicación),
  • intervals_no es el número incremeneted después de cada generación del token (esto probablemente debería resolverse en el servidor mediante la comprobación de un número finito de enteros después de la última exitosa marcada en el pasado)

Cómo usarlo

  1. Generate secret (debe ser el parámetro correcto para base64.b32decode() ) - preferiblemente 16-char (no = signs), ya que seguramente funcionó tanto para script como para Google Authenticator.
  2. Use get_hotp_token() si quiere que las contraseñas de un solo uso se invaliden después de cada uso. En Google Authenticator, este tipo de contraseñas las mencioné como basadas en el contador. Para verificarlo en el servidor deberá verificar varios valores de intervals_no (ya que no tiene ninguna garantía de que el usuario no generó el pase entre las solicitudes por algún motivo), pero no menos que el último intervals_no valor (por lo tanto, probablemente debería guardarlo en algún lugar).
  3. Use get_totp_token() , si desea que un token funcione en intervalos de 30 segundos. Debes asegurarte de que ambos sistemas tengan la hora correcta establecida (lo que significa que ambos generan la misma marca de tiempo de Unix en cualquier momento dado en el tiempo).
  4. Asegúrate de protegerte del ataque de la fuerza bruta. Si se usa una contraseña basada en el tiempo, entonces intentar 1000000 valores en menos de 30 segundos brinda un 100% de probabilidad de adivinar la contraseña. En el caso de los passowrds basados ​​en HMAC (HOTPs), parece ser aún peor.

Ejemplo

Cuando se utiliza el siguiente código para una contraseña única basada en HMAC:

secret = ''MZXW633PN5XW6MZX'' for i in xrange(1, 10): print i, get_hotp_token(secret, intervals_no=i)

obtendrá el siguiente resultado:

1 448400 2 656122 3 457125 4 35022 5 401553 6 581333 7 16329 8 529359 9 171710

que corresponde a los tokens generados por la aplicación Google Authenticator (excepto si son más cortos que 6 signos, la aplicación agrega ceros al principio para alcanzar una longitud de 6 caracteres).

Estoy tratando de usar contraseñas de un solo uso que se pueden generar con la aplicación Google Authenticator .

Lo que Google Authenticator hace

Básicamente, Google Authenticator implementa dos tipos de contraseñas:

  • HOTP - Contraseña de una sola vez basada en HMAC, lo que significa que la contraseña se cambia con cada llamada, de conformidad con RFC4226 , y
  • TOTP : Contraseña de una sola vez basada en el tiempo, que cambia cada 30 segundos (hasta donde yo sé).

Google Authenticator también está disponible como código abierto aquí: code.google.com/p/google-authenticator

Código actual

Estaba buscando soluciones existentes para generar contraseñas HOTP y TOTP, pero no encontré mucho. El código que tengo es el siguiente fragmento responsable de generar HOTP:

import hmac, base64, struct, hashlib, time def get_token(secret, digest_mode=hashlib.sha1, intervals_no=None): if intervals_no == None: intervals_no = int(time.time()) // 30 key = base64.b32decode(secret) msg = struct.pack(">Q", intervals_no) h = hmac.new(key, msg, digest_mode).digest() o = ord(h[19]) & 15 h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000 return h

El problema al que me enfrento es que la contraseña que genero utilizando el código anterior no es la misma que la que se generó con la aplicación Google Authenticator para Android. Aunque probé varios valores de intervals_no (exactamente los primeros 10000, comenzando con intervals_no = 0 ), siendo el secret igual a la clave proporcionada en la aplicación GA.

Preguntas que tengo

Mis preguntas son:

  1. ¿Qué estoy haciendo mal?
  2. ¿Cómo puedo generar HOTP y / o TOTP en Python?
  3. ¿Hay alguna biblioteca de Python existente para esto?

Para resumir: por favor, denme alguna pista que me ayude a implementar la autenticación de Google Authenticator dentro de mi código Python.


Quería una secuencia de comandos python para generar la contraseña de TOTP. Entonces, escribí el script de python. Esta es mi implementación. Tengo esta info en wikipedia y algunos conocimientos sobre HOTP y TOTP para escribir este guión.

import hmac, base64, struct, hashlib, time, array def Truncate(hmac_sha1): """ Truncate represents the function that converts an HMAC-SHA-1 value into an HOTP value as defined in Section 5.3. http://tools.ietf.org/html/rfc4226#section-5.3 """ offset = int(hmac_sha1[-1], 16) binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff return str(binary) def _long_to_byte_array(long_num): """ helper function to convert a long number into a byte array """ byte_array = array.array(''B'') for i in reversed(range(0, 8)): byte_array.insert(0, long_num & 0xff) long_num >>= 8 return byte_array def HOTP(K, C, digits=6): """ HOTP accepts key K and counter C optional digits parameter can control the response length returns the OATH integer code with {digits} length """ C_bytes = _long_to_byte_array(C) hmac_sha1 = hmac.new(key=K, msg=C_bytes, digestmod=hashlib.sha1).hexdigest() return Truncate(hmac_sha1)[-digits:] def TOTP(K, digits=6, window=30): """ TOTP is a time-based variant of HOTP. It accepts only key K, since the counter is derived from the current time optional digits parameter can control the response length optional window parameter controls the time window in seconds returns the OATH integer code with {digits} length """ C = long(time.time() / window) return HOTP(K, C, digits=digits)