T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate_password.py:4
- Finding
- Passwords Generated with a Non-Cryptographic Pseudorandom Number Generator## Vulnerability Details **File Location**: `scripts/generate_password.py`, lines 4–24 **Vulnerability Type**: Use of a cryptographically insecure pseudorandom number generator **Risk Level**: High ### Vulnerable Code ```python import random import string import os from datetime import datetime def generate_password(min_length=12, max_length=16, use_uppercase=True, use_lowercase=True, use_digits=True, use_symbols=True): """生成随机长度随机密码""" # 随机选择长度 length = random.randint(min_length, max_length) chars = '' if use_uppercase: chars += string.ascii_uppercase if use_lowercase: chars += string.ascii_lowercase if use_digits: chars += string.digits if use_symbols: chars += string.punctuation if not chars: chars = string.ascii_letters + string.digits password = ''.join(random.choice(chars) for _ in range(length)) ``` ### Technical Analysis The script uses Python's `random` module for both password length selection and character selection. This module is based on the deterministic Mersenne Twister algorithm and is not designed for passwords, authentication secrets, session tokens, or other security-sensitive values. If an attacker can infer or recover the generator state—for example, by observing enough outputs, accessing the process state, or correlating outputs with other uses of the same process-wide generator—the attacker may predict subsequent generated passwords. The use of a broad character set does not compensate for a predictable random source. In addition, selecting every character independently from one combined pool does not guarantee the documented inclusion of at least one uppercase letter, one lowercase letter, one digit, and one symbol. ### Attack Path 1. A victim invokes the skill to generate a password and uses that password for an account or protected resource. 2. An attacker ...[truncated 1053 chars]
- Remediation
- ## Remediation Suggestions - Replace `random.choice()` with `secrets.choice()`. - Select the length using `secrets.randbelow(max_length - min_length + 1) + min_length`. - Build separate enabled character classes and select at least one character from each enabled class. - Fill the remaining positions using the combined enabled character pool. - Securely randomize the final character order using a cryptographically secure Fisher–Yates shuffle driven by `secrets.randbelow()`. - Validate that `min_length` can accommodate the number of required character classes. - Add tests verifying the length bounds and required character-class guarantees. Example secure design: ```python import secrets import string def secure_shuffle(items): for index in range(len(items) - 1, 0, -1): swap_index = secrets.randbelow(index + 1) items[index], items[swap_index] = items[swap_index], items[index] def generate_password(min_length=12, max_length=16): classes = [ string.ascii_uppercase, string.ascii_lowercase, string.digits, string.punctuation, ] if min_length < len(classes) or max_length < min_length: raise ValueError("Invalid password length constraints") length = min_length + secrets.randbelow(max_length - min_length + 1) characters = [secrets.choice(character_class) for character_class in classes] combined = ''.join(classes) characters.extend( secrets.choice(combined) for _ in range(length - len(characters)) ) secure_shuffle(characters) return ''.join(characters), length ```
