python secrets 模块

secrets 说明

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

生成随机数

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

choice(sequence)

从非空序列中返回一个安全的随机元素。
import secrets

# 假设我们有一个元素列表
elements = ['apple', 'banana', 'cherry', 'date']

# 使用 secrets.choice 来安全地选择列表中的一个随机元素
secure_random_element = secrets.choice(elements)

print(secure_random_element)

randbelow(n)

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

# 设定上界
n = 100

# 生成一个从0到99(包括)的随机整数
random_number = secrets.randbelow(n)

print(random_number)

randbits(k)

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

# 生成一个8比特长度的随机整数
random_bits = secrets.randbits(8)
print("随机比特数:", random_bits)

class secrets.SystemRandom

用操作系统提供的最高质量源生成随机数的类。
类的主要方法:
getrandbits(k):生成一个k比特长度的随机整数。
randrange(start, stop):生成一个介于start到stop之间的随机整数。
import secrets

# 创建SystemRandom实例
sys_random = secrets.SystemRandom()

# 生成一个8比特长度的随机整数
random_bits = sys_random.getrandbits(8)
print("随机比特数:", random_bits)

# 生成一个1到10之间的随机整数
random_int = sys_random.randrange(1, 11)
print("随机整数:", random_int)

生成 Token

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

token_bytes([nbytes=None])

如果指定了 nbytes, secrets.token_bytes(nbytes) 会返回一个包含 nbytes 个随机字节的 bytes 对象。
如果未提供 nbytes 参数,它会返回一个合适用于安全令牌的默认长度的随机字节序列(通常为 32 个字节)。
import secrets

# 生成默认长度(通常为32个字节)的随机字节序列
random_token = secrets.token_bytes()

print(len(random_token))   # 输出:32
print(random_token) # 输出随机字节序列:b'\x8b\xf9\xfe#J\xffHe\x1a\xc5X\x8d1\x14\xff\x99\xba\n\xef9\x98"\xec\xb7\xbb\xfb\x9d0\x0c\xa6\xe1S'

# 生成指定长度的随机字节序列
specified_length_token = secrets.token_bytes(16)

print(specified_length_token) # 输出随机字节序列:b'\x1d\xce\xfc\x04\xc7FT\xfb\xe6\xa7T\xcb\x90\xa0n\x83'

token_hex([nbytes=None])

如果提供了 nbytes 参数,则会生成长度为 nbytes*2 的十六进制字符串(每个字节转换为两个十六进制字符)。
如果未提供 nbytes 参数,则会生成一个适用于安全令牌的默认长度的十六进制字符串(通常是 32 个字符)。
import secrets

# 生成默认长度(通常为32个字符)的随机十六进制字符串
random_hex_token = secrets.token_hex()

print(random_hex_token)

# 生成指定长度的随机十六进制字符串
specified_length_hex_token = secrets.token_hex(16)

print(specified_length_hex_token)

token_urlsafe([nbytes=None])

返回安全的 URL 随机文本字符串,包含 nbytes 个随机字节。文本用 Base64 编码,平均来说,每个字节对应 1.3 个结果字符。
未提供 nbytes 或为 None 时,它会生成一个适合于安全令牌的默认长度随机 URL 安全字符串。

这个函数生成的返回值是一个只包含 URL 安全字符(字母、数字、下划线和短横线)的字符串。
import secrets

# 生成默认长度的随机 URL 安全字符串
random_urlsafe_token = secrets.token_urlsafe()

print(len(random_urlsafe_token))   # 输出:43
print(random_urlsafe_token)        # 输出:L8kP_F_Er-gLrGO_e_JHZBaZjlqXikrqfZFR-NBUSFI

# 生成指定长度的随机 URL 安全字符串
specified_length_urlsafe_token = secrets.token_urlsafe(16)

print(specified_length_urlsafe_token)  # 输出:CzlTfna4b2MaRPsJWoQQbg

比较操作

compare_digest(ab)

用于比较两个字符串 a 和 b,并且在字符串匹配时具有防止时间侧信道攻击的特性。在密码学和安全相关的场景中,比较两个敏感字符串时,使用 secrets.compare_digest 要优于简单的 == 操作符。

该函数返回一个布尔值,如果 a 和 b 匹配,返回 True,否则返回 False。这种比较在比较时间上更加均匀,不易受到时间侧信道攻击的影响。在比较敏感数据时,尤其是在密码验证或令牌比对时使用这个函数,可以提高系统的安全性。
import secrets

a = "my-secret-token-1"
b = "my-secret-token-2"

result = secrets.compare_digest(a, b)
print(result)  # 如果a和b匹配,结果为True;否则为False

生成系统密码示例

import string
import random
import secrets

def generate_password(min_length, max_repeat, max_class_repeat, *char_credits):
    characters = [string.digits, string.ascii_uppercase, '!@#$%^&*(){}<>,?`~+-=[]', string.ascii_lowercase]
    all_characters = ''.join(characters)

    while True:
        # 生成每个字符类别的字符
        password = [secrets.choice(char) for char, count in zip(characters, char_credits) for _ in range(count)]

        # 添加字符以满足最小密码长度要求
        remaining_len = min_length - sum(char_credits)
        password += [secrets.choice(all_characters) for _ in range(remaining_len)]

        random.shuffle(password)

        if is_valid_password(min_length, password, characters, max_repeat, max_class_repeat):
            return ''.join(password)


def is_valid_password(min_length, password, characters, max_repeat, max_class_repeat):
    # 判断是否以=开头
    if ''.join(password).startswith("="):
        return False
    
    # 检查字符和类别是否连续重复超过了规定次数
    repeat_count = max(password.count(char) for char in set(password))
    if repeat_count > min_length // 4:
        return False

    for char_class in characters:
        char_class_positions = [idx for idx, char in enumerate(password) if char in char_class]
        for idx in char_class_positions:
            if idx < len(password) - max_class_repeat:
                if all(password[idx + i] in char_class for i in range(0, max_class_repeat + 1)):
                    return False
    # 判定同一个字符的索引
    char_repeat = [char for char in set(password) if password.count(char) > max_repeat]
    char_repeat_positions = {}
    for idx, char in enumerate(password):
        if char in char_repeat:
            if char not in char_repeat_positions:
                char_repeat_positions[char] = [idx]
            else:
                char_repeat_positions[char].append(idx)

    # 判定相同字符是否连续
    for _, index in char_repeat_positions.items():
        for idx in index:
            if idx < len(password) - max_repeat:
                if all(password[idx + i] == password[idx] for i in range(0, max_repeat + 1)):
                    return False
    return True


if __name__ == '__main__':

    # 设置密码生成的参数
    min_length = 16
    char_credits = (3, 3, 3, 3)  # 数字、大写字母、特殊字符、小写字母
    # 同一个字符不能连续重复的数量
    max_repeat = 2
    # 同一类字符不能连续重复的数量
    max_class_repeat = 3

    # 生成和打印密码
    for _ in range(100):
        generated_password = generate_password(min_length, max_repeat, max_class_repeat, *char_credits)
        print(generated_password)

生成密码重置URL示例

import secrets
import string

# 生成安全令牌,用于密码恢复应用程序
security_token = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(16))

# 构建临时密保URL
temp_url = f"https://example.com/password-recovery?token={security_token}"

print("生成的临时密保URL:", temp_url)

# 输出:生成的临时密保URL: https://example.com/password-recovery?token=Z8eiGfzhCCBQPmIS

参考文档

https://docs.python.org/zh-cn/3.12/library/secrets.html#module-secrets

posted @ 2024-04-15 10:20  小吉猫  阅读(375)  评论(0编辑  收藏  举报