文章目录

  • uu模块
  • 模块方法详解
  • 示例
  • base64模块
  • 模块方法详解
  • binascii模块
  • 模块函数
  • bisect模块
  • 模块函数
  • random模块
  • 简介
  • 簿记功能
  • 整数用方法
  • 序列用方法
  • 实数值分布
  • 替代生成器
  • hashlib模块
  • 哈希算法
  • 模块方法和属性
  • SHAKE算法的可变长度摘要
  • 密钥导出
  • hmac模块
  • 模块方法
  • HMAC对象实例方法
  • secrets模块
  • 随机数
  • 生成令牌
  • 令牌应该使用多少字节?
  • 其他功能
  • 最佳实践


uu模块

模块方法详解

该模块以 uuencode 格式对文件进行编码和解码,允许通过仅使用 ascii 的连接传输任意二进制数据。该模块的编码和解码方法接受文件对象作为参数,但是也可以接受类似文件的对象作为参数。为了向后兼容,也接受包含路径名的字符串,并打开相应的文件进行读写;路径名 - 表示标准输入或输出。但是,该接口已经弃用;调用者最好打开文件本身,并确保在需要时,在Windows上使用 rbwb 模式打开文件。

该模块包含如下两个主要方法:

  • uu.encode(in_file, out_file, name=None, mode=None, *, backtick=False)
    将文件 in_file 的内容使用uuencode进行编码,然后将编码后的内容写入 out_file 文件。
    name 参数用于指定 out_file 文件中起始行中的文件名字段,该文件名字段为重新构建二进制数据时要使用的文件名。如果未指定该参数,则 out_file 文件的起始行中的文件名默认为 in_file 的文件名。
    mode 参数用于指定 out_file 文件中起始行中的模式字段。该参数最好指定为3位8进制数。如果未指定 mode 参数,则 out_file 文件中起始行中的模式字段的值默认为 644
    backtick 参数决定是否使用反引号替换空格来表示新行。该参数值为默认值 False 时,使用空格表示新行,该参数为 True 时,使用反引号表示新行。
  • uu.decode(in_file, out_file, mode=None, quiet=False)
    in_file 中uuencode编码的数据解码,并将解码的数据写入 out_file 文件。
    如果 out_file 参数是表示文件路径的字符串,当 out_file 参数指向的文件必须被创建时, mode 参数用于设置 out_file 参数指向的文件的权限位。默认的 out_filemodein_file 的起始行中读取。如果起始行中指定的文件已经存在,则会引发 uu.Error 异常。
    如果输入是由错误的uuencoder生成的,该方法可能会向标准错误输出警告,并且Python可以从错误中恢复。将 quiet 参数设置为 True 可以使此警告无效。

示例

将文件进行uuencode编码:

>>> import uu
>>> with open('example.txt', 'w') as f:
        f.writelines('this is an uuencode example.\ndo you understand the uuencode?')
>>> uu.encode('example.txt', 'uu_file.txt', 'uu.txt', 0o777, backtick=True)
>>> with open('uu_file.txt') as f:
...     print(f.read())
... 
begin 777 uu.txt
M=&AI<R!I<R!A;B!U=65N8V]D92!E>&%M<&QE+@ID;R!Y;W4@=6YD97)S=&%N
/9"!T:&4@=75E;F-O9&4_
`
end

解码uuencode编码的文件:

>>> uu.decode('uu_file.txt', 'text.txt')
>>> with open('text.txt') as f:
...     print(f.read())
... 
this is an uuencode example.
do you understand the uuencode?

base64模块

此模块提供了将二进制数据编码为可打印的 ASCII 字符以及将这些编码解码回二进制数据的函数。它为 RFC 3548 指定的 Base16, Base32 和 Base64 编码以及已被广泛接受的 Ascii85 和 Base85 编码提供了编码和解码函数。

RFC 3548 编码的目的是使得二进制数据可以作为电子邮件的内容正确地发送,用作 URL 的一部分,或者作为 HTTP POST 请求的一部分。其中的编码算法和 uuencode 是不同的。

此模块提供了两个接口。新的接口提供了从类字节对象到 ASCII 字节 bytes 的编码,以及将 ASCII 的类字节对象 或字符串解码到 bytes 的操作。此模块支持定义在 RFC 3548 中的所有 base-64 字母表 (普通的、URL 安全的和文件系统安全的)。

旧的接口不提供从字符串的解码操作,但提供了操作文件对象的编码和解码函数。旧接口只支持标准的 Base64 字母表,并且按照 RFC 2045 的规范每 76 个字符增加一个换行符。注意:如果你需要支持 RFC 2045,那么使用 email 模块可能更加合适。

模块方法详解

  • base64.b64encode(s, altchars=None)
    bytesbytearraybytes-like 对象 s 编码成 bytes 对象。
    若指定 altchars 参数,则必须为长度为2的 bytes-like 对象,用于替换Base64标准字母表中的 +/
>>> base64.b64encode(b'talk is cheap, show me the code', b'-_')
b'dGFsayBpcyBjaGVhcCwgc2hvdyBtZSB0aGUgY29kZQ=='
  • base64.b64decode(s, altchars=None, validate=False)
    解码Base64编码过的 bytes-like 对象 或ASCII 字符串 s
    若指定 altchars 参数,则必须为长度为2的 bytes-like 对象,用于替换 +/ 字符。
    如果 s 无法被解码,则会引发 binascii.Error 异常。
    如果 validate 值为 False (默认情况),则在填充检查前,将丢弃既不在标准 Base64 字母表之中也不在备用字母表中的字符。如果 validateTrue,这些非 Base64 字符将导致 binascii.Error 异常。
    返回值为 bytes 对象。
>>> base64.b64decode(b'dGFsayBpcyBjaGVhcCwgc2hvdyBtZSB0aGUgY29kZQ==')
b'talk is cheap, show me the code'
>>> # bytes对象中包含非Base64字母表中的字符 “*”,并且validate为True时将引发异常
... base64.b64decode(b'dGFsayBpcyBjaGVhcCwgc2hvdyBtZSB0aGUgY29kZQ==*', validate=True)
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/Users/neochen/.virtualenvs/Test/lib/python3.7/base64.py", line 86, in b64decode
    raise binascii.Error('Non-base64 digit found')
binascii.Error: Non-base64 digit found
>>> # bytes对象中包含非Base64字母表中的字符 “*”,并且validate为默认值False时,可以正常解码
... base64.b64decode(b'dGFsayBpcyBjaGVhcCwgc2hvdyBtZSB0aGUgY29kZQ==*')
b'talk is cheap, show me the code'
  • base64.standard_b64encode(s)
    使用标准Base64字母表编码编码 bytesbytearraybytes-like 对象 s 。返回值为 bytes 对象。
  • base64.standard_b64decode(s)
    使用标准Base64字母表解码 bytes-like 对象或 ASCII 字符串 s。返回值为 bytes 对象。
  • base64.urlsafe_b64encode(s)
    bytesbytearraybytes-like 对象 s 编码成 bytes 对象。
    使用URL安全和文件系统安全的字母表,即使用 -_ 替换 +/
    返回值为 bytes 对象。
  • base64.urlsafe_b64decode(s)
    解码 bytes-like 对象或ASCII 字符串 s,使用URL安全和文件系统安全的字母表,即使用 -_ 替换 +/
    返回值为 bytes 对象。
  • base64.b32encode(s)
    用Base32 编码 bytes-like 对象 s。返回值为 bytes 对象。
>>> base64.b32encode(b'talk is cheap, show me the code')
b'ORQWY2ZANFZSAY3IMVQXALBAONUG65ZANVSSA5DIMUQGG33EMU======'
  • base64.b32decode(s, casefold=False, map01=None)
    解码Base32编码过的 bytes-like 对象或 ASCII 字符串 s
    casefold 的值为 False,则输入只能包含大写字母。若 casefold 的值为 True,则输入可以包含小写字母。
    RFC 3548 允许将数字 0(zero) 映射为大写字母 O,并可以选择是否将数字 1(one) 映射为大写字母 I 或大写字母 L。可选参数 map01 不是 None 时, 数字 0 总是被映射为大写字母 O,map01 参数的值指定数字 1 的映射目标 (大写字母 I 或小写字母 l)。为了安全考虑,默认值被设为 None,这种情况下, 0 和 1 不允许被作为输入。
    返回值为 bytes 类型。
>>> base64.b32decode(b'ORQWY2ZANFZSAY3IMVQXALBAONUG65ZANVSSA5DIMUQGG33EMU======')
b'talk is cheap, show me the code'
>>> # "casefold" 值为默认值 "False" 时,输入中不能包含小写字母
... base64.b32decode(b'ORQWY2ZANFZSAY3IMVQXALBAONUG65ZANVSSA5DIMUQGG33EMU======'.lower())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/neochen/.virtualenvs/Test/lib/python3.7/base64.py", line 231, in b32decode
    raise binascii.Error('Non-base32 digit found') from None
binascii.Error: Non-base32 digit found
>>> # "casefold" 值为 "True" 时,输入可以包含小写字母
... base64.b32decode(b'ORQWY2ZANFZSAY3IMVQXALBAONUG65ZANVSSA5DIMUQGG33EMU======'.lower(), True)
b'talk is cheap, show me the code'
  • base64.b16encode(s)
    用Base16 编码 bytes-like 对象 s。返回值为 bytes 对象。
>>> base64.b16encode(b'talk is cheap, show me the code')
b'74616C6B2069732063686561702C2073686F77206D652074686520636F6465'
  • base64.b16encode(s, casefold=False)
    解码Base16编码过的 bytes-like 对象或 ASCII 字符串 s
    casefold 的值为 False,则输入只能包含大写字母。若 casefold 的值为 True,则输入可以包含小写字母。
>>> base64.b16decode(b'74616C6B2069732063686561702C2073686F77206D652074686520636F6465')
b'talk is cheap, show me the code'
>>> base64.b16decode(b'74616C6B2069732063686561702C2073686F77206D652074686520636F6465'.lower())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/neochen/.virtualenvs/Test/lib/python3.7/base64.py", line 267, in b16decode
    raise binascii.Error('Non-base16 digit found')
binascii.Error: Non-base16 digit found
>>> base64.b16decode(b'74616C6B2069732063686561702C2073686F77206D652074686520636F6465'.lower(), True)
b'talk is cheap, show me the code'
  • base64.encode(input, output)
    从二进制文件对象 input 中读取数据进行Base64编码,将编码的结果写入文件对象 output 中。每76个字节后将插入一个换行符 b'\n’,并且在末尾添加一个换行符 b'\n’
  • base64.decode(input, output)
    从二进制文件对象 input 中读取数据并解码,将解码的结果写入文件对象 output 中。
  • base64.encodebytes(s)
    将包含任意二进制数据的 bytes-like 对象 s 进行Base64编码并将编码后的数据返回。 每76个字节后将插入一个换行符 b'\n’,并且在末尾添加一个换行符 b'\n’
  • base64.decodebytes(s)
    解码 bytes-like 对象 s,并返回解码后的字节。s 中必须包含一行或多行Base64编码的数据,

binascii模块

该模块包含多个在二进制和二进制表示的各种ASCII码之间转换的方法。 通常情况不会直接使用这些函数,而是使用像 uubase64binhex 这样的封装模块。 为了有较高的执行效率,该模块包含许多用 C 写的底层函数,这些底层函数被一些高级模块所使用。

a2b_* 函数接受只含有ASCII码的Unicode字符串。其他函数只接受 bytes-like 对象(如 bytesbytearray 和其他支持缓冲区协议的对象)

模块函数

  • binascii.a2b_uu(string)
    将单行 uu 编码数据转换成二进制数据并返回。uu 编码每行的数据通常包含45 个(二进制)字节,最后一行除外。每行数据后面可能跟有空格。
  • binascii.b2a_uu(data, *, backtick=False)
    将二进制数据转换为 ASCII 编码字符,返回值是转换后的行数据,包括换行符。 data 的长度最多为45。如果 backtickTure,则零由反引号而不是空格表示。
  • binascii.a2b_base64(string)
    将 base64 数据块转换成二进制并以二进制数据形式返回。一次可以传递多行数据。
  • binascii.b2a_base64(data, *, newline=True)
    将二进制数据转换为一行用 Base64 编码的ASCII字符串。返回值是转换后的行数据,如果 newlineTrue,则返回值包括换行符。该函数的输出符合rfc3548。
  • binascii.a2b_qp(data, header=False)
    将一块带引号且可打印的数据转换成二进制数据并返回。一次可以转换多行。如果可选参数 header 存在且为 True,则数据中的下划线将被解码成空格。
  • binascii.b2a_qp(data, quotetabs=False, istext=True, header=False)
    将二进制数据转换为一行或多行带引号且可打印编码的ASCII字符并返回。如果可选参数 quotetabs 存在且为 True,则对所有制表符和空格进行编码。如果可选参数 istext 存在且为 True,则不对新行进行编码,但将对尾随空格进行编码。如果可选参数 header 存在且为 True,则空格将被编码为下划线。如果可选参数 header 存在且为 False,则也会对换行符进行编码;否则换行转换可能会损坏二进制数据流。
  • binascii.a2b_hqx(string)
    将 binhex4 格式的 ASCII 数据不进行 RLE 解压缩直接转换为二进制数据。该字符串应包含完整数量的二进制字节,或者(在binhex4 数据最后部分)剩余位为零。
  • binascii.rledecode_hqx(string)
    根据 binhex4 标准对数据执行 RLE 解压缩。该算法在一个字节的数据后使用 0x90 作为重复指示符,然后计数。计数 0 指定字节值 0x90 。该例程返回解压缩的数据,输入数据以孤立的重复指示符结束的情况下,将引发 Incomplete 异常。
  • binascii.rlecode_hqx(string)
    对数据执行binhex4风格的RLE压缩并返回结果。
  • binascii.b2a_hqx(string)
    执行 hexbin4 类型的二进制到 ASCII 码的转换并返回结果字符串。输入数据应经过 RLE 编码,且数据长度可被3整除(除了最后一个片段)。
  • binascii.crc_hqx(data, value)
    value 作为初始 CRC 计算 data 的16位 CRC 值,返回其结果。这里使用 CRC-CCITT 生成多项式 x16 + x12 + x5 + 1 ,通常表示为 0x1021。该 CRC 被用于 binhex4 格式。
  • binascii.crc32(data[, value])
    计算 CRC-32 ,从 value 的初始 CRC 开始计算 data 的32位校验和。默认初始 CRC 为零。该算法与 ZIP 文件校验和一致。由于该算法被设计用作校验和算法,因此不适合用作通用散列算法。使用方法如下:
print(binascii.crc32(b"hello world"))
# Or, in two pieces:
crc = binascii.crc32(b"hello")
crc = binascii.crc32(b" world", crc)
print('crc32 = {:#010x}'.format(crc))
  • binascii.b2a_hex(data)
    返回二进制数据 data 的十六进制表示形式。 data 的每个字节都被转换为相应的2位十六进制表示形式。因此返回的字节对象的长度是 data 的两倍。
    使用:bytes.hex() 方法也可以方便地实现相似的功能(但仅返回文本字符串)。
  • binascii.hexlify(data)
    功能与 b2a_hex() 函数相同。
  • binascii.a2b_hex(hexstr)
    返回由十六进制字符串 hexstr 表示的二进制数据。此函数功能与 b2a_hex() 相反。 hexstr 必须包含偶数个十六进制数字(可以是大写或小写),否则会引发 Error 异常。
    使用:bytes.fromhex() 类方法也实现相似的功能(仅接受文本字符串参数,不限制其中的空白字符)。
  • binascii.unhexlify(hexstr)
    功能与 a2b_hex() 函数相同。

bisect模块

该模块支持按顺序维护列表,而不必在每次插入之后再对列表进行排序。对于比较长的列表,比较列表中的元素的操作是比较昂贵的,这个模块中的方法是对常见方法的改进。这个模块之所以被称为 bisect,是因为其中的方法使用了二分算法。该模块的源码可以作为二分算法的示例(边界条件已经正确!)

模块函数

  • bisect.bisect_left(a, x, lo=0, hi=len(a))
    返回要插入 a 中的元素 x 的插入点索引,假定 a 是已经排序的。a 可以是 list,tuple,array 等元素可以进行排序的序列或者可迭代对象。
    参数 lohi 可以用来指定列表中需要考虑的子集;默认情况下使用整个列表。如果 x 已经出现在 a 中,插入点将位于 a 中的所有的 x 的前面(在左边)。该方法假设 a 已经排序,返回值适合用作 list.insert() 的第一个参数。
    返回的插入点 i 将数组 a 分成两半,以便左边的所有元素 val 满足(val < x for val in a[lo:i])且右边的所有元素 val 满足(val >= x for val in a[i:hi])。
>>> bisect.bisect_left(range(10), 7)
7
  • bisect.bisect_right(a, x, lo=0, hi=len(a))
    类似于 bisect_left(),但是返回的插入点索引始终位于 a 中所有已存在的 x 的后面(在右边)。
    返回的插入点 i 将数组 a 分成两半,以便左边的所有元素 val 满足(val < x for val in a[lo:i])且右边的所有元素 val 满足(val >= x for val in a[i:hi])。
    返回的插入点 i 将数组 a 分成两半,以便左边的所有元素 val 满足(val <= x for val in a[lo:i])且右边的所有元素 val 满足(val > x for val in a[i:hi])。
>>> bisect.bisect_right(range(10), 7)
8
  • bisect.bisect(a, x, lo=0, hi=len(a))
    bisect_right 函数功能完全相同
  • bisect.insort_left(a, x, lo=0, hi=len(a))
    x 插入 a 中并且保持 a 仍然是有序的。这等价于 a.insert(bisect.bisect_left(a, x, lo, hi), x),假设 a 已经排序。请记住,O(log n)搜索由缓慢的O(n)插入步骤控制。
>>> seq = list(range(10))
>>> bisect.insort_left(seq, 7)
>>> seq
[0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9]
  • bisect.insort_right(a, x, lo=0, hi=len(a))
    insort_left 相似,但会将 x 插入 a 中所有所有 x 的后边(右边)。
>>> seq = list(range(1, 11))
>>> seq
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> bisect.insort_right(seq, 7)
>>> seq
[1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10]
  • bisect.insort(a, x, lo=0, hi=len(a))
    insort_right 函数功能完全一致。

random模块

简介

Python的random模块实现了各种分布的伪随机数生成器。

对于整数,有从一定范围内统一的进行选择的函数。 对于序列,有统一选择随机元素的函数、用于生成列表的随机排列的函数、以及用于随机抽样而无需进行替换的函数。

在实数轴上,有计算均匀、正态(高斯)、对数正态、负指数、伽马和贝塔分布的函数。 要生成角度分布,可以使用 von Mises 分布。

该模块中几乎所有的函数都依赖于基本函数 random,它在半开放区间 [0.0,1.0) 内均匀生成随机浮点数。 Python 使用 Mersenne Twister 作为核心生成器。 它产生 53-bit 精度的浮点数,周期为 2**19937-1 ,其在 C 中的底层实现既快速又线程安全。 Mersenne Twister 是现存最广泛测试的随机数生成器之一。 但是,因为完全确定性,它不适用于所有目的,并且完全不适合于加密目的。

这个模块提供的函数实际上是 random.Random 类的隐藏实例的绑定方法。 你可以自己实例化 Random 类以获取不共享状态的生成器。

如果你想使用自己设计的基础生成器,还可以子类化 Random 类:在这种情况下,重载 random()seed()getstate() 以及 setstate() 方法。新生成器还可以提供 getrandbits() 方法 —— 这允许 randrange() 在任意大的范围内生成选择。

该模块还提供 SystemRandom 类,它使用系统函数 os.urandom() 从操作系统提供的源生成随机数。

警告:不应将此模块的伪随机生成器用于安全目的。 有关安全性或加密用途,请参阅 secrets 模块。

簿记功能

  • random.seed(a=None, version=2)
    初始化随机数生成器。
    如果 aNone ,则使用当前系统时间。 如果操作系统提供随机源,则使用操作系统提供的随机源而不是系统时间(有关可用性的详细信息,请参阅 os.urandom() 函数)。
    如果 a 是 int 类型,则直接使用。
    version 为2时(默认的),strbytesbytearray 对象将被转换为 int 对象并使用它的所有位。
    version 为1时,(用于从旧版本的Python再现随机序列),用于 strbytes 的算法将生成更窄的种子范围。
    注意,当种子值相等时,生成的随机数和随机数的顺序就是固定的,通过下面的例子就可以看出来:
>>> import random
>>> random.seed(100)
>>> for _ in range(10):
...     print(random.randrange(10000))
... 
2386
7528
7453
2863
6439
5730
7102
8304
1801
8731
>>> random.seed(100)
>>> for _ in range(10):
...     print(random.randrange(10000))
... 
2386
7528
7453
2863
6439
5730
7102
8304
1801
8731
>>>
  • random.getstate()
    返回一个表示当前生成器内部状态的对象。这个方法的返回值可以传递给 setstate() 方法用来恢复生成器的状态。
  • random.setstate(state)
    state 应该是调用 getstate() 方法获得的对象。该方法用于将生成器的内部状态恢复到调用 getstate() 方法时的状态。
  • random.getrandbits(k)
    返回 k 位随机的整数。k位随机的整数 的意思是 每一位都是随机数的k位2进制数表示的十进制整数
    例如:
    random.getrandbits(5) 表示一个每一位上都是随机数的5位2进制数,即[0, 32) 范围内的随机整数。
    random.getrandbits(8) 表示一个每一位上都是随机数的8位2进制数,即[0, 256)范围内的随机整数。
    此方法随MersenneTwister 生成器一起提供,其他一些生成器也可以将其作为API的可选部分提供。 如果可用,getrandbits() 将会启用 randrange() 来处理任意大范围。

整数用方法

  • random.randrange(stop)
  • random.randrange(start, stop[, step])
    range(start, stop, step) 返回一个随机选择的元素。相当于 choice(range(start, stop,step)) ,但实际上并没有构建一个 range 对象。
    位置参数模式与 range() 函数相同。不应该使用关键字参数,因为该函数可能以意外的方式使用它们。
  • random.randint(a, b)
    返回随机整数 N 满足 a <= N <= b。相当于 randrange(a, b+1)

序列用方法

  • random.choice(seq)
    从非空序列 seq 返回一个随机元素。 如果 seq 为空,则引发 IndexError
  • random.choices(population, weights=None, *, cum_weights=None, k=1)
    population 中随机选择 k 个元素,返回由这 k 个元素组成的列表。
    参数 populationweightscum_weights 必须是可迭代对象。如果指定了weightscum_weights,则它的长度必须与 population 相同,并且不能同时指定 weightscum_weights 参数。
    如果既未指定 weight 也未指定 cum_weights ,则以相等的概率进行选择。
    如果指定了 weight 参数,则根据相对权重在 populaiton 中进行选择。 或者,如果给出 cum_weights 序列,则根据累积权重(可能使用 itertools.accumulate() 计算)进行选择。 例如,相对权重 [10, 5, 30, 5] 相当于累积权重 [10, 15, 45, 50]。 在内部,相对权重在进行选择之前会转换为累积权重,因此提供累积权重可以节省工作量。
    weightscum_weights 中的元素可以使用任何与 random() 返回的 float 值互操作的数值类型(包括整数,浮点数和分数但不包括十进制小数)。
    对于给定的种子,具有相等加权的 choices() 函数通常产生与重复调用 choice() 不同的序列。 choices() 使用的算法使用浮点运算来实现内部一致性和速度。 choice() 使用的算法默认为重复选择的整数运算,以避免因舍入误差引起的小偏差。
  • random.shuffle(x[, random])
    将序列 x 随机打乱位置。
    可选参数 random 是一个0参数函数,用于在 [0.0, 1.0) 中返回随机浮点数;默认使用 random.random() 函数。
    要改变一个不可变的序列并返回一个新的打乱列表,请使用 sample(x, k=len(x))
    请注意,即使对于长度比较小的序列 xx 的排列总数也可以快速增长,大于大多数随机数生成器的周期。 这意味着长序列的大多数排列永远不会产生。 例如,长度为2080的序列是可以在 Mersenne Twister 随机数生成器的周期内拟合的最大序列。
  • random.sample(population, k)
    从可迭代对象 population 中随机选择 k 个元素,每个元素只能选择一次,然后返回由这 k 个元素组成的列表。 用于无重复的随机抽样。
    返回包含来自 population 的元素的新列表,同时保持 population 不变。 结果列表按选择顺序排列,因此所有子切片也将是有效的随机样本。 这允许抽奖获奖者(样本)被划分为大奖和第二名获胜者(子切片)。
    population 中的元素不必是唯一的或可哈希的。 如果 population 中包含重复元素,则重复元素的每次出现都是样本中可能的选择。
    要从一系列整数中选择样本,请使用 range() 对象作为参数。 对于从大量人群中采样,这种方法特别快速且节省空间:sample(range(10000000), k=60)
    如果样本大小大于 population 大小,则引发 ValueError

实数值分布

以下函数生成特定的实值分布。如常用数学实践中所使用的那样, 函数参数以分布方程中的相应变量命名;大多数这些方程都可以在任何统计学教材中找到。

  • random.random()
    返回 [0.0, 1.0) 范围内的下一个随机浮点数。
  • random.uniform(a, b)
    返回一个随机浮点数 N ,当 a <= ba <= N <= b ,当 b < ab <= N <= a 。根据 a + (b-a) * random() 中的浮点舍入,终点 b 可以包括或不包括在该范围内。
  • random.triangular(low, high, mode)
    返回一个随机浮点数 N ,使得 low <= N <= high 并在这些边界之间使用指定的 modelowhigh 边界默认为0和1。 mode 参数默认为边界之间的中点,给出对称分布。
  • random.betavariate(alpha, beta)
    Beta 分布。 参数的条件是 alpha > 0beta > 0。 返回值的范围介于 0 和 1 之间。
  • random.expovariate(lambd)
    指数分布。 lambd 是 1.0 除以所需的平均值,它应该是非零的。 (该参数本应命名为 lambda ,但由于 lambda 已经是 Python 中的关键字,所以改为 lamdb)如果 lambd 为正,则返回值的范围为 0 到正无穷大;如果 lambd 为负,则返回值从负无穷大到 0。
  • random.gammavariate(alpha, beta)
    Gamma 分布。 ( 不是 gamma 函数! ) 参数的条件是 alpha > 0beta > 0
    概率分布函数是:
pdf(x) = (x ** (alpha - 1) * math.exp(-x / beta)) / math.gamma(alpha) * beta ** alpha
  • random.gauss(mu, sigma)
    高斯分布。 mu 是平均值,sigma 是标准差。 这比下面定义的 normalvariate() 函数略快。
  • random.lognormvariate(mu, sigma)
    对数正态分布。 如果你采用这个分布的自然对数,你将得到一个正态分布,平均值为 mu 和标准差为 sigmamu 可以是任何值,sigma 必须大于零。
  • random.normalvariate(mu, sigma)
    正态分布。 mu 是平均值,sigma 是标准差。
  • random.vonmisesvariate(mu, kappa)
    mu 是平均角度,以弧度表示,介于0和 2*pi 之间,kappa 是浓度参数,必须大于或等于零。 如果 kappa等于零,则该分布在0到 2**pi* 的范围内减小到均匀的随机角度。
  • random.paretovariate(alpha)
    帕累托分布。 alpha 是形状参数。
  • random.weibullvariate(alpha, beta)
    威布尔分布。 alpha 是比例参数,beta 是形状参数。

替代生成器

  • class random.Random([seed])
    该类实现了该模块所使用的默认伪随机数生成器。
  • class random.SystemRandom([seed])
    使用 os.urandom() 函数的类,用从操作系统提供的源生成随机数。 这并非适用于所有系统。 也不依赖于软件状态,序列不可重现。 因此,seed() 方法没有效果而被忽略。 getstate()setstate() 方法如果被调用则引发 NotImplementedError 异常。

hashlib模块

该模块为许多不同的安全散列和消息摘要算法实现了一个公共接口。包括FIPS安全哈希算法SHA1、SHA224、SHA256、SHA384和SHA512(在FIPS 180-2中定义)以及RSA的MD5算法(在Internet RFC 1321中定义)。术语 “安全散列” 和 “消息摘要” 是可以互换的。较老的算法称为消息摘要。现代术语是安全散列。

哈希算法

每种哈希类型都有一个构造函数。所有返回的哈希对象都具有相同的简单接口。例如:使用 sha256() 创建一个SHA-256散列对象,现在可以使用 *update()*方法向这个对象提供 bytes-like 对象(通常是 bytes)。在任何时候,都可以使用 digest()hexdigest() 方法向它请求数据连接的摘要。

该模块中始终存在的哈希算法构造函数有 sha1()、sha224()、sha256()、sha384()、sha512()、blake2b()blake2s()md5() 通常也是可用的,但如果使用罕见的Python “FIPS兼容” 构建,则可能会缺少 md5()。根据Python在平台上使用的OpenSSL库,还可能提供其他算法。在大多数平台上,sha3_224()、sha3_256()、sha3_384()、sha3_512()、shake_128()shake_256() 也是可用的。

模块方法和属性

该模块提供了以下常量属性:

  • hashlib.algorithms_guaranteed
    包含此模块保证在所有平台上支持的哈希算法名称的集合。注意,尽管一些上游供应商提供了一个奇怪的 “符合FIPS” 的Python构建而将其排除在外,但是 md5 仍然包含在这个列表中。
>>> hashlib.algorithms_guaranteed
{'sha3_384', 'sha384', 'sha3_512', 'sha3_256', 'sha3_224', 'blake2b', 'md5', 'shake_256', 'blake2s', 'sha1', 'sha224', 'sha256', 'sha512', 'shake_128'}
  • hashlib.algorithms_available
    包含在运行的Python解释器中可用的哈希算法名称的集合。这些名称将在传递给 new() 时被识别。
    algorithms_guaranteed 总是该属性的一个子集。相同的算法可能会在这个集合中以不同的名称出现多次(多亏了OpenSSL)。
>>> hashlib.algorithms_available
{'sha3_384', 'ecdsa-with-SHA1', 'blake2s', 'sha1', 'MD5', 'sha256', 'sha3_256', 'ripemd160', 'md4', 'blake2b', 'SHA384', 'shake_256', 'SHA512', 'dsaEncryption', 'sha224', 'SHA1', 'dsaWithSHA', 'sha', 'sha512', 'shake_128', 'sha3_224', 'md5', 'MD4', 'MDC2', 'SHA256', 'RIPEMD160', 'sha384', 'mdc2', 'DSA', 'SHA', 'DSA-SHA', 'whirlpool', 'SHA224', 'sha3_512'}
>>> hashlib.algorithms_guaranteed.issubset(hashlib.algorithms_available)
True

要计算散列值,需要先调用以下命名构造函数,其名称与所表示的算法是相同的:

  • md5() MD5散列(128位)
  • sha1() SHA1散列(160位)
  • sha224() SHA224散列(224位)
  • sha256() SHA256散列(256位)
  • sha384() SHA384散列(384位)
  • sha512() SHA512散列(512位)

这些函数返回的摘要对象实例 d 有以下方法和属性:

  • d.update(data)
    使用新数据更新散列。data 必须是 bytes-likes 对象。重复调用与使用串联的数据进行单次调用的效果相同。
  • d.digest()
    将摘要作为原始字节字符串返回。
  • d.hexdigest()
    返回文本字符串,摘要的值编码为一系列十六进制数字。
  • d.copy()
    返回摘要副本,副本保留原摘要的内部状态。
  • d.digest_size
    返回散列的字节大小。
  • d.block_size
    散列算法的内部块字节大小。
  • d.name
    返回当前摘要对象所使用的哈希算法的规范命名。始终为小写,并且始终可以作为 new() 方法的参数用以生成新的摘要对象。
>>> d = hashlib.sha256()  # 使用"SHA256"算法初始化一个散列对象
>>> d
<sha256 HASH object @ 0x10d5f9b98>
>>> d.update(b'hello world')  # 计算"hello world"的散列值
>>> d.digest()  # 获取表示"hello world"摘要的字节字符串
b"\xb9M'\xb9\x93M>\x08\xa5.R\xd7\xda}\xab\xfa\xc4\x84\xef\xe3zS\x80\xee\x90\x88\xf7\xac\xe2\xef\xcd\xe9"
>>> d.hexdigest()  # 获取使用十六进制数字表示的"hello world"摘要的字符串
'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'
>>> d.digest_size  # 获取摘要的字节大小
32
>>> d.block_size  # 获取摘要的内部块字节大小
64
>>> d1 = hashlib.sha256()
>>> d1.update(b'hello')
>>> d1.update(b' world')
>>> d1.hexdigest()
'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'
>>> d.hexdigest() == d1.hexdigest()  # 可以看到,使用 'b"hello"' 和 'b" world"'重复调用"update()" 方法与使用 'b"hello world"'单次单次调用"update()" 方法得到的摘要是相同的
True

该模块还提供了一个可选的构造方法:

  • hashlib.new(name[, data])
    这是一个通用的构造函数,name 是表示散列算法名称的字符串,可选的 data 参数用以指定用于更新散列的数据,必须是 bytes-like 对象。它还允许访问上面列出的散列以及OpenSSL库可能提供的任何其他算法。命名构造函数比该构造函数快得多,应该作为首选。
>>> d2 = hashlib.new('sha256', b'hello world')
>>> d2.hexdigest()
'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'

SHAKE算法的可变长度摘要

shake_128shake_256 提供长度可变的摘要,因此,它们的 digest()hex_digest() 方法需要一个用来指定长度的参数 length,并且最大长度不受SHAKE算法的限制。

  • d.digest(length)
  • d.hexdigest(length)
>>> d3 = hashlib.new('shake_256', b'hello world')
>>> d3
<_sha3.shake_256 object at 0x10d451bd0>
>>> d3.hexdigest(36)
'369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d5'
>>> d3.digest(36)
b"6\x97q\xbb,\xb9\xd2\xb0L\x1dT\xcc\xa4\x87\xe3r\xd9\xf1\x87\xf7?{\xa3\xf6[\x95\xc8\xeew\x98\xc5'\xf4\xf3\xc2\xd5"

密钥导出

密钥派生和密钥拓展算法是为安全的密码哈希而设计的。诸如 sha1(password) 这样的简单算法不能抵抗暴力攻击。一个好的密码哈希函数必须是可调谐的、 速度慢的,并且包括一个 salt

  • hashlib.pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None)
    该函数提供了基于 PKCS#5 密码的密钥派生函数2。它使用 HMAC 作为伪随机函数。
    字符串 hash_nameHMAC 的散列摘要算法的期望名称,例如。sha1sha256
    passwordsalt 必须是bytesbytearray 对象。应用程序和库应该将 password 限制为合理的长度(例如1024)。salt 应该来自一个合适的源,例如 os.urandom(),大约16个或更多字节。
    iterations 应根据哈希算法和计算能力来选择。截至2013年,建议至少进行 100000 次SHA-256迭代。
    dklen 是派生键的长度。如果 dklenNone,则使用散列算法 hash_name 的摘要大小,例如SHA-512使用64。
>>> import hashlib, binascii
>>> dk = hashlib.pbkdf2_hmac('sha256', b'password', b'salt', 100000)
>>> binascii.b2a_hex(dk)
b'0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5'
  • hashlib.scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)
    该函数提供了RFC 7914中定义的基于scrypt密码的密钥派生函数。
    passwordsalt 必须是 bytes-like 对象。应用程序和库应该将密码限制为合理的长度(例如1024)。salt 应该来自一个合适的源,例如 os.urandom(),大约16个或更多字节。
    n 是CPU/内存的成本因子,r 是块大小,p 是并行化因子,maxmem 限制内存(OpenSSL 1.1.0默认为32 MiB)。dklen 是派生键的长度。

hmac模块

该模块实现 RFC 2104 中描述的 HMAC算法。

模块方法

该模块包含如下两个方法:

  • hmac.new(key, msg=None, digestmod=None)
    返回一个新的 hmac 对象。key 必须是 bytesbytearray 对象,用于提供密钥。如果指定了 msg 参数,则调用 update(msg) 方法。digestmod 是要使用的HMAC对象的摘要名称、摘要构造函数或模块。它支持任何适合于 hashlib.new() 的名称,并默认为 hashlib.md5() 构造函数。

注意:MD5作为 digestmod 的默认的摘要从3.4 版本开始不推荐使用,3.8 版本将会移除。

>>> d = hmac.new(hashlib.new('md5', b'password').digest(), b'hello world', 'sha256')
>>> d
<hmac.HMAC object at 0x10d906c18>
>>> d.hexdigest()
'09b75fd147dd72e4618366e530034ca57f97a2158e0b439af9a26f7008a3e6fe'
  • hmac.digest(key, msg, digest)
    返回表示给定密钥和摘要的消息的摘要的 bytes 对象。该函数等价于 HMAC(key, msg, digest).digest(),但是使用了优化的C或内联实现,这对于适合内存的消息更快。参数 keymsgdigestnew() 中的含义相同。
    CPython实现细节,优化后的C实现只在摘要是一个字符串和一个摘要算法的名称时使用,该算法由OpenSSL支持。
>>> digest = hmac.digest(hashlib.new('md5', b'password').digest(), b'hello world', 'sha256')
>>> digest
b'\t\xb7_\xd1G\xddr\xe4a\x83f\xe50\x03L\xa5\x7f\x97\xa2\x15\x8e\x0bC\x9a\xf9\xa2op\x08\xa3\xe6\xfe'

HMAC对象实例方法

一个HMAC对象包含如下实例方法和实例属性:

  • HMAC.update(msg)
    使用 msg 更新hmac对象。重复调用与使用串联的参数进行单次调用的效果相同,即:
    m.update(a);m.update(b)m.update(a + b) 等价。
  • HMAC.digest()
    返回到目前为止传递给 update() 方法的字节摘要。这个 bytes 对象的长度将与给定给构造函数的摘要的digest_size 相同。它可以包含非ASCII字节,包括空字节。

警告:在验证例程中将 digest() 的输出与外部提供的摘要进行比较时,建议使用 compare_digest() 函数而不是 == 操作符来降低定时攻击的脆弱性。

  • HMAC.hexdigest()
    digest() 类似,只是摘要作为字符串返回,长度是只包含十六进制数字长度的两倍。这可以用于在电子邮件或其他非二进制环境中安全地交换值。

警告:在验证例程中将 hexdigest() 的输出与外部提供的摘要进行比较时,建议使用 compare_digest() 函数而不是 == 操作符来降低定时攻击的脆弱性。

  • HMAC.copy()
    返回hmac对象的副本(克隆)。这可以用来有效地计算共享一个公共初始子字符串的字符串摘要。
  • HMAC.compare_digest(a, b)
    用于比较两个摘要。如果相同,则返回 True,如果不同,则返回 False。该函数使用了一种设计方法,通过避免基于内容的短路行为来防止计时分析,使其适合于密码学。ab 必须具有相同的类型:要么是str(仅限ASCII,例如由 HMAC.hexdigest() 返回),要么是 bytes-like 对象。

注意:如果 ab 的长度不同,或者发生错误,定时攻击理论上可以揭示关于 ab 的类型和长度的信息,但不能揭示它们的值。

>>> digest1 = hmac.digest(hashlib.new('md5', b'password').digest(), b'hello world1', 'sha256')
>>> digest2 = hmac.digest(hashlib.new('md5', b'password').digest(), b'hello world2', 'sha256')
>>> hmac.compare_digest(digest1, digest2)
False
  • HMAC.digest_size
    HMAC对象实例的摘要的的字节长度。
  • HMAC.block_size
    哈希算法的内部块的字节大小。
  • HMAC.name
    HMAC的规范名称。通常是小写。例如 hmac-md5

secrets模块

该模块用于生成适用于管理密码、帐户身份验证、安全令牌和相关机密等数据的加密强随机数。

特别是,在random模块中,应该优先使用 secret,而不是默认的伪随机数生成器,它是为建模和仿真而设计的,而不是安全性或密码学。

随机数

secret模块提供对操作系统提供的最安全的随机源的访问。

  • class secrets.SystemRandom
    使用操作系统提供的最高质量源生成随机数的类。请查看 random.SystemRandom 了解更多细节。
  • secrets.choice(sequence)
    从非空序列中返回一个随机选择的元素。
  • secrets.randbelow(n)
    返回在 range(0, n) 范围内的一个随机整数。
  • secrets.randbits(k)
    返回 k 位随机的整数。k位随机的整数 的意思是 每一位都是随机数的k位2进制数表示的十进制整数

生成令牌

该模块提供了生成安全令牌的函数。适用于密码重置、难以猜测的URL等应用程序。

  • secrets.token_bytes([nbytes=None])
    返回长度为 nbytes 的随机字节字符串。如果未提供 nbytes 参数或者参数值为 None,则会使用一个合适的默认值。
>>> secrets.token_bytes(16)
b'Gd\xf0q+4\x7f\xbd.\xa1\xfe\x80r\xbd\x95\xa0'
  • secrets.token_hex([nbytes=None])
    返回使用十六进制数字表示的随机文本字符串,字符串包含 nbytes 随机字节,每个字节转换为两个十六进制数字。如果未指定 nbytes 参数或者参数值为 None,则使用一个合理的默认值。
>>> secrets.token_hex(16)
'a261cdadc2469a7383a21f325ab26f21'
  • secrets.token_urlsafe([nbytes=None])
    返回一个随机的URL安全文本字符串,其中包含 nbytes 随机字节。文本是Base64编码的,因此平均每个字节大约产生1.3个字符。如果未指定 nbytes 参数或者参数值为 None,则使用一个合理的默认值。
>>> token_urlsafe(16)  
'Drmhze6EPcv0fN_81Bj-nA'

令牌应该使用多少字节?

为了防止暴力攻击,令牌需要具有足够的随机性。不幸的是,随着计算机的性能变得越来越强大,并且能够在更短的时间内做出更多的猜测,被认为足够的量必然会增加。截至2015年,人们认为32字节(256位)的随机性足以满足secret模块的典型用例。

对于那些希望管理自己的令牌长度的人,可以通过为各种 token_* 函数提供 nbytes 参数显式地指定令牌的随机性。该参数被视为要使用的随机字节数。

否则,如果没有提供参数,或者参数为 None,则 token_* 函数将使用合理的默认值。

注意:该缺省值随时可能更改,包括在维护发布期间。

其他功能

  • secrets.compare_digest(a, b)
    如果字符串 ab 相等,则返回 True,否则返回 False
    使用该方法可以降低定时攻击的风险。有关更多细节,请参见 hmac.compare_digest()

最佳实践

本节将介绍使用 secrets 模块管理基本安全级别的方法和最佳实践。

生成一个8个字符的字母数字密码:

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

注意:应用程序不应该以可恢复的格式存储密码,无论是纯文本还是加密的。应该使用加密强单向(不可逆)哈希函数对它们进行加盐和哈希。

生成包含至少一个小写字符、至少一个大写字符和至少三个数字的10个字符的字母数字密码:

>>> import string
>>> from secrets import choice
>>> def get_token():
...     alphabet = string.ascii_letters + string.digits
...     while True:
...         password = ''.join(choice(alphabet) for _ 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):
...             return password
... 
>>> print(get_token())
7xca361JhZ

生成一个 XKCD风格的密码

>>> from secrets import choice
>>> # 在标准的Linux系统上,使用一个方便的字典文件。其他平台可能需要提供自己的单词列表。
... with open('/usr/share/dict/words') as f:
...     words = [word.strip() for word in f]
...     password = ' '.join(choice(words) for i in range(4))
... 
>>> password
'Macaranga moveability underwrought trimesinic'

生成一个难以猜测的临时URL,其中包含适合密码恢复应用程序的安全令牌:

>>> url = 'https://mydomain.com/reset=' + secrets.token_urlsafe()
>>> url
'https://mydomain.com/reset=2VzXzxF6fYkw7VCmkZy2_r7PeLpQCqBzrrrhRvouOE8'