在Python中生成密码
我想在Python中生成一些字母数字密码。有几种可能的方法:
import string
from random import sample, choice
chars = string.ascii_letters + string.digits
length = 8
''.join(sample(chars, length)) # first way
''.join(choice(chars) for i in range(length)) # second way
不过我不喜欢第一种方法,因为它只选择了唯一的字符,这样就不能生成长度大于字符数量的密码。而我也不喜欢第二种方法,因为里面有一个没用的变量i
。有没有其他好的选择呢?
12 个回答
这里有两个使用内置的 secrets 库的例子(适用于 Python 3.6 及以上版本)
1. secrets.token_urlsafe
这个方法比其他推荐的方式要快得多。(下面有时间对比)
import secrets
password = secrets.token_urlsafe(32)
示例输出:
4EPn9Z7RE3l6jtCxEy7CPhia2EnYDEkE6N1O3-WnntU
对于 token_urlsafe
,你需要传入的参数是字节数。一般来说,一个字节大约对应1.3个字符(经过 base64 编码)。
2. 强制要求数字/大写字母等的数量
这个是从 secrets 文档稍微修改过来的。通过这个方法,你可以更精确地控制生成的密码应该是什么样子的。当然,如果你需要生成很多密码,这个方法就不那么快了。
- 强制长度为 20 个字符
- 至少包含 4 个小写字母
- 至少包含 4 个大写字母
- 至少包含 4 个数字
- 可以在
alphabet
中添加特殊字符。在这个例子中,只添加了-
和_
。
import string
import secrets
alphabet = string.ascii_letters + string.digits + '-_'
while True:
password = ''.join(secrets.choice(alphabet) for i in range(20))
if (sum(c.islower() for c in password) >=4
and sum(c.isupper() for c in password) >=4
and sum(c.isdigit() for c in password) >=4):
break
示例输出:
HlxTm2fcFE54JA1I_Yp5
3. "我不需要那么精细的控制"
如果考虑速度,你也可以省略 while 循环。在这种情况下,实际上可以简化为 gerrit 的答案(但这样你就失去了更精细的控制):
import string
import secrets
alphabet = string.ascii_letters + string.digits + '-_'
password = ''.join(secrets.choice(alphabet) for i in range(20))
速度对比
1. secrets.token_urlsafe
1.62 µs ± 96.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
2. 强制要求数字/大写字母等的数量
107 µs ± 11.9 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
3. "我不需要那么精细的控制"
77.2 µs ± 9.31 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
速度对比的设置:在 Win10 上使用 Python 3.8.5 64 位,每个密码包含 43 个字符(相当于 32 字节用于 token_urlsafe)。
对于那些关注加密伪随机数生成器的人:
def generate_temp_password(length):
if not isinstance(length, int) or length < 8:
raise ValueError("temp password must have positive length")
chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
from os import urandom
# original Python 2 (urandom returns str)
# return "".join(chars[ord(c) % len(chars)] for c in urandom(length))
# Python 3 (urandom returns bytes)
return "".join(chars[c % len(chars)] for c in urandom(length))
请注意,为了确保分布均匀,chars
字符串的长度应该是 128 的整除数;否则,你需要找到其他方法来从这个范围内均匀选择。
在 Python 3.6 及以上版本中,你应该使用 secrets 模块 来生成安全的密码,这样的密码更不容易被破解。这个信息是从官方文档中整理过来的:
import secrets
import string
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(20)) # for a 20-character password
想了解更多关于密码生成的技巧和最佳实践,可以查看 Python 文档中的这个部分。你也可以考虑添加一些 string.punctuation
中的符号。