Python中生成随机数的标准方法是什么?

29

有人可以分享在Python中为OAuth请求创建nonce的最佳实践吗?

5个回答

27

尽管这个问题创建时可能还不存在,但Python 3.6引入了secrets模块,旨在生成密码,账户认证,安全令牌和相关机密数据等管理所需的加密强度随机数。

在此情况下,可以轻松生成一个nonce(这里是一个base64编码字符串):

nonce = secrets.token_urlsafe()

可供选择的是token_bytes用于获取二进制标记或token_hex用于获取十六进制字符串。


最近我听说过nonce。据我所知,nonce应该是整数类型,即数字。但是在这里,您正在尝试使用base64编码的字符串作为nonce。那么nonce也可以是一个字符串吗? - Santhosh
@SanthoshYedidi nonce 是一个数字,取决于令牌提供者的实现方式(它不是 JWT 标准的严格部分)。我见过的所有提供者都很乐意使用字符串。重要的是它是一个单次使用的值。只增加的数字是好的,但你必须解决系统重置的问题。使用一个大的随机字符串通常更容易。 - Erdős-Bacon
是的,没错,很难跟踪增量。 - Santhosh

17

对于大多数实际目的,这提供了非常好的一次性随机数:

import uuid
uuid.uuid4().hex
# 'b46290528cd949498ce4cc86ca854173'

uuid4() 使用的是 Python 中最好的随机函数 os.urandom()

Nonce 应该只使用一次,并且难以预测。请注意,uuid4()uuid1() 更难以预测,而后者更具全局唯一性。因此,可以通过组合它们来获得更强的安全性:

uuid.uuid4().hex + uuid.uuid1().hex
# 'a6d68f4d81ec440fb3d5ef6416079305f7a44a0c9e9011e684e2c42c0319303d'

1
如果你认为uuid1()更具全球唯一性,因为它基于mac地址,那么你应该知道实际上mac地址远非唯一。一些制造商会为给定的批次/型号使用相同的mac地址...所以基于这种假设,你的说法是错误的。相反地,情况恰恰相反。 - comte
1
@comte 不仅如此,UUID1 还包含时间戳。只要世界上的某些计算机具有不同的 MAC 地址,MAC 就会增加一定程度的唯一性。来源 - andruso

15

以下是 python-oauth2 的做法:

def generate_nonce(length=8):
    """Generate pseudorandom number."""
    return ''.join([str(random.randint(0, 9)) for i in range(length)])

他们还拥有以下内容:

@classmethod
def make_nonce(cls):
    """Generate pseudorandom number."""
    return str(random.randint(0, 100000000))

此外,还存在这个问题:“make_nonce不够随机”,该问题提议:

def gen_nonce(length):
   """ Generates a random string of bytes, base64 encoded """
   if length < 1:
      return ''
   string=base64.b64encode(os.urandom(length),altchars=b'-_')
   b64len=4*floor(length,3)
   if length%3 == 1:
      b64len+=2
   elif length%3 == 2:
      b64len+=3
   return string[0:b64len].decode()

还有参考CVE-2013-4347。简短版总结,使用os.urandom或抽象接口(SystemRandom)。

我喜欢我的lambda,并且不希望非字母数字字符出现,所以我使用了这个:

lambda length: filter(lambda s: s.isalpha(), b64encode(urandom(length * 2)))[:length]

获取字符串也可以直接使用uuid1().get_hex()。但你可能更想要使用uuid4uuid5,关于Python符合的UUID标准的更多信息可以在RFC4122中找到。 - A T
当然,除非你想要种子是当前主机和时间,否则你可以使用uuid1:https://docs.python.org/2/library/uuid.html 。get_hex()很有用,谢谢! - radtek
1
请注意,如果您正在尝试生成名称中带有“nonce”的任何内容,则重要的是使用 os.urandom(),而决不能使用 random.randomrandom.randint。否则,您可能会遇到严重的安全问题。 - Aur Saraf
2
"nonce" 是由密码学家发明的一个新词,用于明确地表示“没有人能够猜到的值”(实际上是足够大且具有加密安全性的随机整数)。他们在协议中以许多创造性的方式使用它们,并始终基于没有人能够猜测它们的假设。使用 random.randint 很容易被坏人猜测,从而危及安全。https://www.cigital.com/papers/download/developer_gambling.php - Aur Saraf
1
@kevr 我有点晚回答,但是对于未来的读者:Eric 的意思是 4*floor(length/3)(floor 应该是 ceil,但这是另一个问题)。参考资料:https://en.wikipedia.org/wiki/Base64#Output_padding 和这里的数学解释 https://dev59.com/S2Yr5IYBdhLWcg3wy9SA - scmanjarrez
显示剩余4条评论

4
这里是rauth的功能。这里没有太多的硬性规定,规范似乎也没有太多的偏见。您需要保证值是唯一的。除此之外,在不影响服务提供商的前提下,您可以使用任何方法。

请注意,现在在将其传递给sha1之前,它还调用.encode('ascii')。可能是为了与Python 3兼容? - A T

1

这里有一些与Emailage相关的想法。generate_nonce是他们代码中的内容,但我使用generate_nonce_timestamp并使用了uuid。它给我一个随机的字母数字字符串和时间戳(以秒为单位):

import random
import time
import uuid


def generate_nonce(length=8):
    """Generate pseudo-random number."""
    return ''.join([str(random.randint(0, 9)) for i in range(length)])


def generate_timestamp():
    """Get seconds since epoch (UTC)."""
    return str(int(time.time()))

def generate_nonce_timestamp():
    """Generate pseudo-random number and seconds since epoch (UTC)."""
    nonce = uuid.uuid1()
    oauth_timestamp, oauth_nonce = str(nonce.time), nonce.hex
    return oauth_nonce, oauth_timestamp

我喜欢使用uuid1,因为它基于当前主机和时间生成uuid,并且具有您可以提取的时间属性,如果您需要两者。对于emailage,您需要时间戳和nonce。
这是您得到的内容:
>>> generate_nonce_timestamp()
('a89faa84-6c35-11e5-8a36-080027c336f0', '136634341422770820')

如果你想要移除 -,使用 nonce.get_hex()。 uuid1 - 从主机ID、序列号和当前时间生成UUID。更多信息请参见 uuid

1
请看我对 @AT 关于随机数和 random.randint 的评论(简而言之:永远不要用)。 - Aur Saraf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接