Python Snippets

Secure Password Generator with Customizable Complexity

import secrets
import string
import argparse
from typing import List, Optional

def generate_secure_password(
    length: int = 16,
    include_uppercase: bool = True,
    include_lowercase: bool = True,
    include_digits: bool = True,
    include_symbols: bool = True,
    exclude_ambiguous: bool = True
) -> str:
    """
    Generate a cryptographically secure password with customizable complexity.
    
    Args:
        length: Length of the password (default: 16)
        include_uppercase: Include uppercase letters (default: True)
        include_lowercase: Include lowercase letters (default: True)
        include_digits: Include digits (default: True)
        include_symbols: Include special symbols (default: True)
        exclude_ambiguous: Exclude ambiguous characters like 0, O, l, 1 (default: True)
    
    Returns:
        A secure password string
    """
    if length < 4:
        raise ValueError("Password length must be at least 4 characters")
    
    # Build character set based on parameters
    characters = ""
    
    if include_uppercase:
        uppercase = string.ascii_uppercase
        if exclude_ambiguous:
            uppercase = uppercase.replace('O', '').replace('I', '')
        characters += uppercase
    
    if include_lowercase:
        lowercase = string.ascii_lowercase
        if exclude_ambiguous:
            lowercase = lowercase.replace('l', '').replace('o', '')
        characters += lowercase
    
    if include_digits:
        digits = string.digits
        if exclude_ambiguous:
            digits = digits.replace('0', '').replace('1', '')
        characters += digits
    
    if include_symbols:
        symbols = "!@#$%^&*()_-+=[]{}|;:,.<>?"
        characters += symbols
    
    if not characters:
        raise ValueError("At least one character type must be included")
    
    # Ensure at least one character from each selected type is included
    required_chars: List[str] = []
    
    if include_uppercase:
        required_chars.append(secrets.choice(string.ascii_uppercase if not exclude_ambiguous 
                                           else string.ascii_uppercase.replace('O', '').replace('I', '')))
    
    if include_lowercase:
        required_chars.append(secrets.choice(string.ascii_lowercase if not exclude_ambiguous 
                                           else string.ascii_lowercase.replace('l', '').replace('o', '')))
    
    if include_digits:
        required_chars.append(secrets.choice(string.digits if not exclude_ambiguous 
                                           else string.digits.replace('0', '').replace('1', '')))
    
    if include_symbols:
        required_chars.append(secrets.choice("!@#$%^&*()_-+=[]{}|;:,.<>?"))
    
    # Generate remaining characters
    remaining_length = length - len(required_chars)
    password_chars = required_chars + [secrets.choice(characters) for _ in range(remaining_length)]
    
    # Shuffle the password to avoid predictable patterns
    secrets.SystemRandom().shuffle(password_chars)
    
    return ''.join(password_chars)

def main():
    """Command-line interface for password generation."""
    parser = argparse.ArgumentParser(description="Generate secure passwords")
    parser.add_argument("-l", "--length", type=int, default=16, help="Password length (default: 16)")
    parser.add_argument("-n", "--no-uppercase", action="store_true", help="Exclude uppercase letters")
    parser.add_argument("-w", "--no-lowercase", action="store_true", help="Exclude lowercase letters")
    parser.add_argument("-d", "--no-digits", action="store_true", help="Exclude digits")
    parser.add_argument("-s", "--no-symbols", action="store_true", help="Exclude symbols")
    parser.add_argument("-a", "--allow-ambiguous", action="store_true", help="Allow ambiguous characters")
    parser.add_argument("-c", "--count", type=int, default=1, help="Number of passwords to generate")
    
    args = parser.parse_args()
    
    try:
        for i in range(args.count):
            password = generate_secure_password(
                length=args.length,
                include_uppercase=not args.no_uppercase,
                include_lowercase=not args.no_lowercase,
                include_digits=not args.no_digits,
                include_symbols=not args.no_symbols,
                exclude_ambiguous=not args.allow_ambiguous
            )
            print(f"Password {i+1}: {password}")
    except ValueError as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    main()

What This Code Does

This snippet provides a secure password generator that creates cryptographically strong passwords using Python’s secrets module, which is designed for generating cryptographically strong random numbers suitable for managing data such as passwords, account authentication, and security tokens.

The generator offers several customization options:

Why It’s Useful

  1. Security: Uses the secrets module instead of random, which is cryptographically secure
  2. Flexibility: Customizable to meet different password requirements
  3. Compliance: Helps generate passwords that meet various security policies
  4. Usability: Command-line interface makes it easy to use in scripts or manually
  5. Reliability: Guarantees character diversity and avoids predictable patterns

How to Run It

Save the code to a file (e.g., password_generator.py) and run it in several ways:

Basic usage:

python password_generator.py

Generates one 16-character password with all character types.

Generate multiple passwords:

python password_generator.py -c 5

Generates 5 passwords.

Customize password properties:

python password_generator.py -l 20 --no-symbols -c 3

Generates 3 passwords, 20 characters long, without special symbols.

Exclude ambiguous characters:

python password_generator.py -a

Generates passwords that include potentially ambiguous characters like 0 and O.

The generator ensures each password contains at least one character from each selected character type, making it both secure and compliant with most password policies.