什么是双因素验证 2FA,如何用 Python 实现?

传统的用户名密码方式,容易泄漏,并不安全。

你说,加上短信验证码不就安全了,其实短信验证码也是不安全的,容易被拦截和伪造,SIM 卡也可以克隆,已经有案例,先伪造身份证,再申请一模一样的手机号码,把钱转走。

因此就有了 Two-factor authentication,简称 2FA,也就是双因素验证。最常见的就是用户名密码,再加一个动态码。动态码通常由随身携带的移动设备上生成,比如 U 盾、手机。

动态码最常见的实现算法就是 One-Time Password(OTP),是基于时间的一次性密码,它是公认的可靠解决方案,已经写入国际标准 RFC6238。比如我们最常用的 Google Authenticator,就是 OTP。

那么,知道了 2FA,接下来应该考虑的事,就是如何让你用 Python 写的网站实现 2FA。

轮子其实已经有了,那就是 PyOTP,结合自己的理解,分享一下它的用法。

1、安装

pip 安装,不多说。

pip install pyotp

2、配对

配对就是移动设备和我们的 web 服务器配对。

首先,在服务器上使用如下代码生成一次性密钥:

>>> import pyotp
>>> pyotp.random_base32()
'BZ4TOJD4JWWJWPQBKP23FA55EMHA25Y2'
>>>

然后将这个密钥以二维码的形式让手机(移动设备)扫描,扫描之后,手机上的应用就保存了这个密钥。

3、验证

接下来,在手机上就可以使用下面这段逻辑产生动态密码了。

>>> totp = pyotp.TOTP('BZ4TOJD4JWWJWPQBKP23FA55EMHA25Y2')
>>> totp.now()
'941782'
>>>

现在,你有 30 秒的时间将这个 6 位数密码提交到服务器(输入到网页上),服务器服务器也使用同样的密钥和当前时间戳,生成一个动态码,跟用户提交的动态码比对。只要两者不一致,就验证失败,也就是下面这段逻辑,如果超过 30 秒,(当然,30 秒可以自定义),也会失效:

>>> totp = pyotp.TOTP('BZ4TOJD4JWWJWPQBKP23FA55EMHA25Y2')
>>> totp.verify('941782')
True
>>> time.sleep(30)
>>> totp.verify('941782')
False

除了使用基于时间的动态密码,也可以用基于计数的动态密码,逻辑如下:

hotp = pyotp.HOTP('base32secret3232')
hotp.at(0) # => '260182'
hotp.at(1) # => '055283'
hotp.at(1401) # => '316439'

# OTP verified with a counter
hotp.verify('316439', 1401) # => True
hotp.verify('316439', 1402) # => False

4、那是不是手机上还有开发个 app ?

不用,我们直接使用现成的 Google Authenticator 就可以了。

如果你已安装了 Google Authenticator,请点击 ➕ 添加密钥,然后扫描下发的二维码,就可以生成动态码:

ee72ae865270a4fb68be6de4f451b600.jpeg

然后你可以执行下面这段代码,就可以发现 pyotp 产生的动态码和 Google Authenticator 产生的是一致的:

import pyotp
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
print("Current OTP:", totp.now())

其实二维码的信息就是 JBSWY3DPEHPK3PXP。

这样,我们用 Google Authenticator 来产生动态密码,网站上进行二次验证,就可以实现 2FA 了。

最后

本文分享了什么是 2FA 以及如何用 Python 实现。如果觉得有收获,还请点「在看」分享给更多的人。如果有问题,欢迎留言讨论。新来的朋友可以关注下,可以给你提供技术支持。

67a2ba811be3ac6a191ce80cb124c8ef.jpeg

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现SM2算法需要用到椭圆曲线加密算法和哈希算法,这里我们使用Python内置的`hashlib`和`cryptomath`模块来实现。 首先,我们需要定义椭圆曲线的参数和基点: ```python P = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF A = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC B = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93 N = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123 Gx = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE171BEEB6E3A9D4F5B Gy = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A ``` 然后,我们需要实现点加和点倍运算: ```python def add(p1, p2): if p1 is None: return p2 if p2 is None: return p1 x1, y1 = p1 x2, y2 = p2 if x1 == x2 and y1 != y2: return None if x1 == x2: m = (3 * x1 * x1 + A) * cryptomath.invmod(2 * y1, P) % P else: m = (y1 - y2) * cryptomath.invmod(x1 - x2, P) % P x3 = (m * m - x1 - x2) % P y3 = (m * (x1 - x3) - y1) % P return (x3, y3) def mul(k, p): if k == 0 or p is None: return None if k == 1: return p if k % 2 == 0: return mul(k // 2, add(p, p)) else: return add(p, mul(k - 1, p)) ``` 接下来,我们需要实现签名和验签的函数: ```python def sign(msg, d): e = int(hashlib.sha256(msg).hexdigest(), 16) k = func.random_int_range(1, N) while True: x, y = mul(k, (Gx, Gy)) r = (e + x) % N if r == 0 or k >= N: k = func.random_int_range(1, N) continue s = (cryptomath.invmod(k, N) * (d * r + k * e)) % N if s != 0: break k = func.random_int_range(1, N) return r, s def verify(msg, q, r, s): e = int(hashlib.sha256(msg).hexdigest(), 16) if r <= 0 or r >= N or s <= 0 or s >= N: return False t = (r + s) % N if t == 0: return False x, y = add(mul(s, (Gx, Gy)), mul(t, q)) if x is None or y is None: return False if (r + x) % N == e: return True else: return False ``` 最后,我们可以用以下代码来测试SM2算法的加解密: ```python d = func.random_int_range(1, N) q = mul(d, (Gx, Gy)) msg = b'Hello, world!' # 签名和验签 r, s = sign(msg, d) if verify(msg, q, r, s): print('Signature verified.') else: print('Signature verification failed.') # 加密和解密 plaintext = b'Hello, world!' k = func.random_int_range(1, N) x1, y1 = mul(k, (Gx, Gy)) c1 = hex(x1)[2:].rjust(64, '0') + hex(y1)[2:].rjust(64, '0') s = mul(N - d, add((int(c1[:64], 16), int(c1[64:], 16)), mul(k, plaintext))) c2 = hex(s[0])[2:].rjust(64, '0') + hex(s[1])[2:].rjust(64, '0') ciphertext = bytes.fromhex(c1 + c2) x2, y2 = mul(d, (int(ciphertext[:64], 16), int(ciphertext[64:128], 16))) decrypted_text = (s[1] - y2) // x2 print(decrypted_text.to_bytes((decrypted_text.bit_length() + 7) // 8, 'big')) ``` 需要注意的是,这里生成的私钥和公钥都是随机生成的,加密和解密的明文和密文都是字节串。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值