我想对我的问题设置一个赏金,但我已经成功地创建了解决方案。我的问题似乎与secret密钥的值不正确有关(它必须是base64.b32decode()函数的正确参数)。
下面我将发布完整的工作解决方案,并解释如何使用它。
代码
下面的代码就足够了。我还把它作为一个单独的模块上传到了GitHub上,这个模块名为onetimepass (可以在这里找到:https://github.com/tadeck/onetimepass)。
代码语言:javascript运行复制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)它有两个功能:
get_hotp_token()生成一次性令牌(单次使用后应失效),get_totp_token()根据时间生成令牌(以30秒为间隔更改),
参数
当涉及参数时:
secret是服务器(上面的脚本)和客户端(谷歌验证器,通过在应用程序中将其作为密码提供)已知的保密值,intervals_no是每次生成令牌后递增的数字(这可能会在服务器上通过在过去最后一次成功检查后检查一些有限数量的整数来解决)
如何使用它
生成secret (它必须是base64.b32decode()的正确参数)-最好是16个字符(没有=符号),因为它肯定适用于脚本和谷歌Authenticator.
Use get_hotp_token(),如果你想在每次使用后使一次性密码无效的话。在Google Authenticator中,我提到的这类密码是基于计数器的。为了在服务器上检查它,您将需要检查intervals_no的几个值(因为您不能保证用户由于某种原因没有生成请求之间的传递),但不能小于最后一个工作的intervals_no值(因此,如果您希望一个令牌以30秒的间隔工作,您可能应该将其存储为somewhere).
Use get_totp_token()。你必须确保两个系统都设置了正确的时间(这意味着它们在任何给定的时刻都会生成相同的Unix时间戳)。
确保保护自己免受暴力破解攻击。如果使用基于时间的密码,那么在不到30秒的时间内尝试1000000个值将有100%的机会猜到密码。在基于HMAC的密码(HOTP)的情况下,情况似乎更糟。
示例
使用以下代码作为基于HMAC的一次性密码时:
代码语言:javascript运行复制secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
print i, get_hotp_token(secret, intervals_no=i)您将得到以下结果:
代码语言:javascript运行复制1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710这与Google Authenticator应用程序生成的令牌相对应(如果短于6个符号,则应用程序会在开头添加零,以达到6个字符的长度)。