代码之家  ›  专栏  ›  技术社区  ›  Martin v. Löwis

随机的。选择不是随机的

  •  13
  • Martin v. Löwis  · 技术社区  · 16 年前

        chars = string.ascii_letters + string.digits
        cookie = ''.join([random.choice(chars) for x in range(32)])
    

    生成不同的cookies。假设RNG是从/dev/uradom播种的,并且随机数序列来自Mersenne twister,我预计碰撞的可能性几乎为零。

    但是,我确实看到了定期的冲突,即使在任何时候只有少数(<100)用户登录。

    为什么随机数不是更随机?

    5 回复  |  直到 16 年前
        1
  •  13
  •   Glenn Maynard    16 年前

    它不应该生成重复项。

    import random
    chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    def gen():
        return ''.join([random.choice(chars) for x in range(32)])
    
    test = [gen() for i in range(100000)]
    print len(test), len(set(test)) # 100000 100000
    

    使用chars=“ab”时,重复出现的可能性很大;在1000000次迭代中有126次重复。这是不存在的62。

    也就是说,这不是生成cookie的好方法,因为会话cookie需要不可预测,以避免涉及窃取他人会话cookie的攻击。Mersenne捻线机不是为生成安全随机数而设计的。我就是这么做的:

    import os, hashlib
    def gen():
        return hashlib.sha1(os.urandom(512)).hexdigest()
    
    test = [gen() for i in range(100000)]
    print len(test), len(set(test))
    

        2
  •  4
  •   orip    16 年前

    • 32个字符,每个字符有62个选项,相当于190位(log2(62)*32)
    • 根据生日悖论,你应该每2**95饼干就自然收到一次碰撞,这意味着永远不会

    这可能是并发问题吗?

    • 如果是,请使用不同的 random.Random 每个线程的实例
    • 可以将这些实例保存在线程本地存储中( threading.local()
    • 在linux上,Python应该使用 os.urandom()
        3
  •  1
  •   bobince    16 年前
    1. random._inst

    2. 也许可以进行一些调试,以检查它是否正确地从Uradom播种,而不是退回到不太严格的基于时间的种子?

    回复:老兄!那就是我被难住了;如果RNG在启动时总是有不同的状态,我看不出你怎么可能会发生冲突。奇怪的我想,为了调查导致碰撞的特殊情况,必须进行大量的状态日志记录,这听起来像是在日志中进行大量的工作。可能是(1a)FCGI服务器通常不会分叉,但偶尔会分叉(可能是在负载或其他情况下)?

        4
  •  0
  •   ilya n.    16 年前

    我不得不删除我原来的答案,这表明发电机不是从种子 /dev/urandom source (对于Python 3.x)明确指出:

    def seed(self, a=None):
        """Initialize internal state from hashable object.
    
        None or no argument seeds from current time or from an operating
        system specific randomness source if available.
    
        If a is not None or an int or long, hash(a) is used instead.
        """
    
        if a is None:
            try:
                a = int(_hexlify(_urandom(16)), 16)
            except NotImplementedError:
                import time
                a = int(time.time() * 256) # use fractional seconds
    
        super().seed(a)
        self.gauss_next = None
    

    因此,我谦卑地承认,世界上有一些我可能无法破译的奥秘。

        5
  •  -4
  •   pvoosten    16 年前

    避免该问题的另一种方法是持有私钥,并使用私钥的(例如MD5)校验和,并将计数器值加入其中。碰撞的概率将非常低。为了更安全,在校验和中添加一些变量,如当前时间、用户的ip地址。。。

    存在生成cookie的库。任何WSGI实现都可能包含cookie生成器。