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)
要点总结
- secrets模块用于密码学安全随机数
- random模块仅用于非安全场景(游戏、模拟)
- token_urlsafe推荐用于令牌生成
- compare_digest用于安全比较(防时序攻击)
- SystemRandom提供random模块的安全替代
📝 发现内容有误?点击此处直接编辑