全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-19 8 分钟 ✍️ juanwangdev

Python安全的随机数生成

密码学安全场景必须使用secrets模块,而非random模块。理解两者区别至关重要。

random vs secrets

Python
import random
import secrets

# random: 非密码学安全
# 基于 Mersenne Twister 算法,可预测
random_value = random.randint(1, 100)

# secrets: 密码学安全
# 基于操作系统安全随机源
secure_value = secrets.randbelow(100) + 1

# 使用场景对比
# | 场景 | 模块 | 原因 |
# |------|------|------|
# | 游戏、模拟 | random | 性能好,不需要安全 |
# | 密码、令牌 | secrets | 不可预测 |
# | 会话ID | secrets | 防止会话劫持 |
# | 加密密钥 | secrets | 防止密钥泄露 |

secrets 模块核心功能

Python
import secrets

# 生成安全整数
num = secrets.randbelow(100)        # 0-99
num_range = secrets.randbelow(100) + 1  # 1-100

# 生成安全比特数
bits = secrets.randbits(64)         # 64位随机数

# 生成安全字节串
bytes_val = secrets.token_bytes(16) # 16字节随机数据

# 生成URL安全字符串(推荐用于令牌)
token = secrets.token_urlsafe(32)   # base64编码,约43字符

# 生成十六进制字符串
hex_token = secrets.token_hex(16)   # 32字符十六进制
Python
# 各类型生成对比
import secrets

print(f"randbelow(100): {secrets.randbelow(100)}")
print(f"randbits(64): {secrets.randbits(64)}")
print(f"token_bytes(16): {secrets.token_bytes(16)}")
print(f"token_urlsafe(32): {secrets.token_urlsafe(32)}")
print(f"token_hex(16): {secrets.token_hex(16)}")

安全令牌生成

Python
import secrets
from datetime import datetime, timedelta

class TokenGenerator:
    "安全令牌生成器"

    @staticmethod
    def generate_api_key(length: int = 32) -> str:
        "生成API密钥"
        return secrets.token_urlsafe(length)

    @staticmethod
    def generate_session_id() -> str:
        "生成会话ID"
        return secrets.token_urlsafe(32)

    @staticmethod
    def generate_reset_token() -> str:
        "生成密码重置令牌"
        return secrets.token_urlsafe(32)

    @staticmethod
    def generate_otp(length: int = 6) -> str:
        "生成一次性密码"
        return ''.join(str(secrets.randbelow(10)) for _ in range(length))

    @staticmethod
    def generate_csrf_token() -> str:
        "生成CSRF令牌"
        return secrets.token_urlsafe(32)

# 使用
generator = TokenGenerator()
api_key = generator.generate_api_key()
session_id = generator.generate_session_id()
otp = generator.generate_otp()

安全密码生成

Python
import secrets
import string

class PasswordGenerator:
    "安全密码生成器"

    ALPHABET = string.ascii_letters + string.digits + string.punctuation

    @staticmethod
    def generate(length: int = 16) -> str:
        "生成安全密码"
        return ''.join(secrets.choice(PasswordGenerator.ALPHABET) for _ in range(length))

    @staticmethod
    def generate_with_requirements(length: int = 16) -> str:
        "生成满足要求的密码"
        # 确保包含各类字符
        password = [
            secrets.choice(string.ascii_uppercase),  # 大写
            secrets.choice(string.ascii_lowercase),  # 小写
            secrets.choice(string.digits),           # 数字
            secrets.choice(string.punctuation),      # 特殊字符
        ]

        # 填充剩余长度
        remaining = length - len(password)
        for _ in range(remaining):
            password.append(secrets.choice(PasswordGenerator.ALPHABET))

        # 打乱顺序
        secrets.SystemRandom().shuffle(password)

        return ''.join(password)

    @staticmethod
    def generate_memorable(word_count: int = 4) -> str:
        "生成易记密码(单词组合)"
        # 常用单词列表(实际应用中使用更完整的列表)
        words = [
            'apple', 'banana', 'cherry', 'dragon',
            'eagle', 'falcon', 'guitar', 'house',
            'igloo', 'jungle', 'kite', 'lemon'
        ]

        selected = [secrets.choice(words) for _ in range(word_count)]
        # 添加分隔符和数字
        separator = secrets.choice(['-', '_', '.'])
        number = secrets.choice(string.digits)

        return f"{separator.join(selected)}{number}"

# 使用
secure_pwd = PasswordGenerator.generate_with_requirements(20)
memorable_pwd = PasswordGenerator.generate_memorable()

加密密钥生成

Python
import secrets
import os

class KeyGenerator:
    "加密密钥生成器"

    @staticmethod
    def generate_aes_key(bits: int = 256) -> bytes:
        "生成AES密钥"
        if bits not in [128, 192, 256]:
            raise ValueError("AES密钥长度必须是128、192或256位")
        return secrets.token_bytes(bits // 8)

    @staticmethod
    def generate_salt(length: int = 16) -> bytes:
        "生成盐值"
        return secrets.token_bytes(length)

    @staticmethod
    def generate_iv(length: int = 16) -> bytes:
        "生成初始化向量"
        return secrets.token_bytes(length)

    @staticmethod
    def generate_nonce(length: int = 12) -> bytes:
        "生成nonce"
        return secrets.token_bytes(length)

# 使用
aes_key = KeyGenerator.generate_aes_key()
salt = KeyGenerator.generate_salt()

安全选择与比较

Python
import secrets

# 安全选择(从序列中选择)
options = ['option_a', 'option_b', 'option_c']
selected = secrets.choice(options)

# 安全比较(防时序攻击)
# 用于比较哈希值、令牌等
token_a = secrets.token_hex(32)
token_b = secrets.token_hex(32)

# 比较是否相等(防时序攻击)
is_equal = secrets.compare_digest(token_a, token_b)

# 正确验证令牌的方式
def verify_token(provided_token: str, expected_token: str) -> bool:
    "安全验证令牌"
    return secrets.compare_digest(provided_token, expected_token)

# 错误方式(容易被时序攻击)
# return provided_token == expected_token

令牌验证系统

Python
import secrets
from datetime import datetime, timedelta

class SecureTokenSystem:
    "安全令牌系统"

    def __init__(self):
        self.tokens = {}  # 存储:token -> (data, expires_at)

    def create_token(self, data: str, expires_in: int = 3600) -> str:
        "创建令牌"
        token = secrets.token_urlsafe(32)
        expires_at = datetime.now() + timedelta(seconds=expires_in)

        self.tokens[token] = {
            'data': data,
            'expires_at': expires_at,
            'used': False
        }

        return token

    def verify_token(self, provided_token: str) -> str:
        "验证令牌"
        for stored_token, info in self.tokens.items():
            if secrets.compare_digest(provided_token, stored_token):
                if datetime.now() > info['expires_at']:
                    return None  # 已过期
                if info['used']:
                    return None  # 已使用
                return info['data']

        return None  # 无效令牌

    def consume_token(self, token: str) -> str:
        "使用令牌(一次性)"
        data = self.verify_token(token)
        if data:
            # 标记为已使用
            for stored_token in self.tokens:
                if secrets.compare_digest(token, stored_token):
                    self.tokens[stored_token]['used'] = True
                    break
        return data

    def cleanup_expired(self):
        "清理过期令牌"
        now = datetime.now()
        expired = [
            t for t, info in self.tokens.items()
            if now > info['expires_at']
        ]
        for t in expired:
            self.tokens.pop(t)

token_system = SecureTokenSystem()
token = token_system.create_token('user_data')
data = token_system.verify_token(token)

secrets.SystemRandom

Python
import secrets

# SystemRandom 是 random.Random 的安全版本
secure_random = secrets.SystemRandom()

# 支持 random 模块的大部分方法
value = secure_random.randint(1, 100)
float_val = secure_random.random()
choice = secure_random.choice(['a', 'b', 'c'])
sample = secure_random.sample(range(100), 10)
shuffle_list = [1, 2, 3, 4, 5]
secure_random.shuffle(shuffle_list)

# 但使用密码学安全的随机源

# 对比
import random
random.seed(42)  # 可预测
print(random.randint(1, 100))  # 固定值

# secrets.SystemRandom 没有 seed 方法
# secure_random.seed(42)  # AttributeError

实际应用示例

Python
import secrets
import string

class SecureIdGenerator:
    "安全ID生成器"

    @staticmethod
    def generate_uuid_like() -> str:
        "生成类似UUID的标识符"
        hex_chars = string.hexdigits.lower()
        parts = [
            ''.join(secrets.choice(hex_chars) for _ in range(8)),
            ''.join(secrets.choice(hex_chars) for _ in range(4)),
            ''.join(secrets.choice(hex_chars) for _ in range(4)),
            ''.join(secrets.choice(hex_chars) for _ in range(4)),
            ''.join(secrets.choice(hex_chars) for _ in range(12)),
        ]
        return '-'.join(parts)

    @staticmethod
    def generate_short_id(length: int = 8) -> str:
        "生成短ID"
        chars = string.ascii_uppercase + string.digits
        return ''.join(secrets.choice(chars) for _ in range(length))

    @staticmethod
    def generate_file_token() -> str:
        "生成文件令牌"
        return secrets.token_urlsafe(16)

# 使用
uuid_like = SecureIdGenerator.generate_uuid_like()
short_id = SecureIdGenerator.generate_short_id()

验证码生成

Python
import secrets
import string

class VerificationCode:
    "验证码生成"

    @staticmethod
    def generate_numeric(length: int = 6) -> str:
        "数字验证码"
        return ''.join(str(secrets.randbelow(10)) for _ in range(length))

    @staticmethod
    def generate_alphanumeric(length: int = 8) -> str:
        "字母数字验证码"
        chars = string.ascii_uppercase + string.digits
        return ''.join(secrets.choice(chars) for _ in range(length))

    @staticmethod
    def generate_mixed_case(length: int = 8) -> str:
        "大小写混合验证码"
        chars = string.ascii_letters + string.digits
        return ''.join(secrets.choice(chars) for _ in range(length))

# 使用
sms_code = VerificationCode.generate_numeric(6)
email_code = VerificationCode.generate_alphanumeric(8)

要点总结

  1. secrets模块用于密码学安全随机数
  2. random模块仅用于非安全场景(游戏、模拟)
  3. token_urlsafe推荐用于令牌生成
  4. compare_digest用于安全比较(防时序攻击)
  5. SystemRandom提供random模块的安全替代

📝 发现内容有误?点击此处直接编辑

← 上一篇 Python加密与解密操作
下一篇 → Python安全编码最佳实践
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库