"secrets" --- 生成管理密码的安全随机数
**************************************

3.6 新版功能.

**源代码:** Lib/secrets.py

======================================================================

"secrets" 模块用于生成高度加密的随机数，适于管理密码、账户验证、安全凭
据及机密数据。

最好用 "secrets" 替代 "random" 模块的默认伪随机数生成器，该生成器适用
于建模和模拟，不宜用于安全与加密。

参见: **PEP 506**


随机数
======

"secrets" 模块是操作系统提供的最安全地随机性来源。

class secrets.SystemRandom

   用操作系统提供的最高质量源生成随机数的类。详见
   "random.SystemRandom"。

secrets.choice(sequence)

   返回一个从非空序列中随机选取的元素。

secrets.randbelow(n)

   返回 [0, *n*) 范围内的随机整数。

secrets.randbits(k)

   返回 *k* 个随机比特位的整数。


生成 Token
==========

"secrets" 模块提供了生成安全 Token 的函数，适用于密码重置、密保 URL 等
应用场景。

secrets.token_bytes([nbytes=None])

   返回含 *nbytes* 个字节的随机字节字符串。如果未提供 *nbytes*，或
   *nbytes* 为 "None"，则使用合理的默认值。

      >>> token_bytes(16)  
      b'\xebr\x17D*t\xae\xd4\xe3S\xb6\xe2\xebP1\x8b'

secrets.token_hex([nbytes=None])

   返回十六进制随机文本字符串。字符串有 *nbytes* 个随机字节，每个字节
   转换为两个十六进制数码。未提供 *nbytes* 或为 "None" 时，则使用合理
   的默认值。

      >>> token_hex(16)  
      'f9bf78b9a18ce6d46a0cd2b0b86df9da'

secrets.token_urlsafe([nbytes=None])

   返回安全的 URL 随机文本字符串，包含 *nbytes* 个随机字节。文本用
   Base64 编码，平均来说，每个字节对应 1.3 个结果字符。未提供 *nbytes*
   或为 "None" 时，则使用合理的默认值。

      >>> token_urlsafe(16)  
      'Drmhze6EPcv0fN_81Bj-nA'


Token 应当使用多少个字节？
--------------------------

为了在面对 暴力攻击 时保证安全，Token 的随机性必须足够高。随着计算机推
衍能力的不断提升，随机性的安全标准也要不断提高。比如 2015 年，32 字节
（256 位）的随机性对于 "secrets" 模块的典型用例就已经足够了。

要自行管理 Token 长度的用户，可以通过为 "token_*" 函数指定 "int" 参数
显式指定 Token 要使用多大的随机性。该参数以字节数表示随机性大小。

反之，如果未提供参数，或参数为 "None"，则 "token_*" 函数将使用合理的默
认值。

注解:

  该默认值随时可能会改变，比如，版本更新的时候。


其他功能
========

secrets.compare_digest(a, b)

   如果字符串或 *字节型对象* *a* 与 *b* 相等则返回 "True"，否则返回
   "False"，使用了“常数时间比较”来降低 定时攻击 的风险。请参阅
   "hmac.compare_digest()" 了解更多细节。


应用技巧与最佳实践
==================

本节展示了一些使用 "secrets" 管理基本安全级别的应用技巧和最佳实践。

生成长度为八个字符的字母数字密码：

   import string
   import secrets
   alphabet = string.ascii_letters + string.digits
   password = ''.join(secrets.choice(alphabet) for i in range(8))

注解:

  应用程序不可 以可恢复的格式存储密码，无论是纯文本还是加密形式。 它们
  应当使用高加密强度的单向（不可逆）哈希函数来加盐和生成哈希值。

生成长度为十个字符的字母数字密码，包含至少一个小写字母，至少一个大写字
母以及至少三个数字：

   import string
   import secrets
   alphabet = string.ascii_letters + string.digits
   while True:
       password = ''.join(secrets.choice(alphabet) for i in range(10))
       if (any(c.islower() for c in password)
               and any(c.isupper() for c in password)
               and sum(c.isdigit() for c in password) >= 3):
           break

生成 XKCD 风格的密码串：

   import secrets
   # On standard Linux systems, use a convenient dictionary file.
   # Other platforms may need to provide their own word-list.
   with open('/usr/share/dict/words') as f:
       words = [word.strip() for word in f]
       password = ' '.join(secrets.choice(words) for i in range(4))

生成临时密保 URL，包含密码恢复应用的安全 Token：

   import secrets
   url = 'https://example.com/reset=' + secrets.token_urlsafe()
