Privacy Tools Guide

Online password generators are a bad idea: you trust a website with your fresh credential, and the site’s JavaScript may log or transmit what it generates. Strong password generation should happen locally, with cryptographically secure randomness, in tools you can inspect.

The Problem with Web-Based Generators

Command-Line Generators

Using /dev/urandom

# 24-character alphanumeric
cat /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 24; echo

# With special characters
cat /dev/urandom | tr -dc 'A-Za-z0-9!@#$%^&*()-_=+' | head -c 32; echo

# Hex (useful for API keys)
openssl rand -hex 32

# Base64 (more compact, good for tokens)
openssl rand -base64 32

Python (secrets module — CSPRNG)

# Simple secure password
python3 -c "import secrets, string; \
  chars = string.ascii_letters + string.digits + '!@#\$%^&*()'; \
  print(''.join(secrets.choice(chars) for _ in range(32)))"

# URL-safe token
python3 -c "import secrets; print(secrets.token_urlsafe(32))"

# Diceware passphrase from system wordlist
python3 -c "
import secrets
words = open('/usr/share/dict/words').read().splitlines()
words = [w for w in words if w.isalpha() and 4 <= len(w) <= 8]
passphrase = ' '.join(secrets.choice(words) for _ in range(6))
print(passphrase)
"

pwgen

sudo apt install pwgen

# 20-char secure random password
pwgen -s 20 1

# 10 passwords of 32 chars with special chars
pwgen -sy 32 10

KeePassXC CLI

sudo apt install keepassxc

keepassxc-cli generate --length 32 --upper --lower --numbers --special

Diceware — Maximum Trust

Diceware passphrases use physical dice, removing all digital entropy concerns:

# EFF wordlist (7,776 words)
curl -o eff_large_wordlist.txt \
  https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt

python3 -c "
import secrets
words = {}
with open('eff_large_wordlist.txt') as f:
    for line in f:
        parts = line.strip().split()
        if len(parts) == 2:
            words[parts[0]] = parts[1]

def roll(n=5):
    return ''.join(str(secrets.randbelow(6) + 1) for _ in range(n))

passphrase = ' '.join(words[roll()] for _ in range(6))
print(passphrase)
"

6 EFF words = ~77.5 bits of entropy. Use 7-8 words for master passwords.

Entropy Comparison

Method Length Entropy
All lowercase 12 chars ~56 bits
Alphanumeric 20 chars ~119 bits
Full charset 16 chars ~104 bits
6 EFF words ~30 chars ~77 bits
8 EFF words ~40 chars ~103 bits
openssl rand -base64 32 44 chars 256 bits

Shell Script Password Generator

#!/bin/bash
# passgen.sh

usage() {
    echo "Usage: $0 [-l length] [-t type] [-n count]"
    echo "  -t: alpha, alnum, full, hex, b64"
    exit 1
}

LENGTH=24; TYPE="full"; COUNT=1

while getopts "l:t:n:h" opt; do
    case $opt in
        l) LENGTH=$OPTARG ;;
        t) TYPE=$OPTARG ;;
        n) COUNT=$OPTARG ;;
        h) usage ;;
    esac
done

for _ in $(seq 1 $COUNT); do
    case $TYPE in
        alpha) python3 -c "import secrets, string; print(''.join(secrets.choice(string.ascii_letters) for _ in range($LENGTH)))" ;;
        alnum) python3 -c "import secrets, string; print(''.join(secrets.choice(string.ascii_letters+string.digits) for _ in range($LENGTH)))" ;;
        full)  python3 -c "import secrets, string; a=string.ascii_letters+string.digits+'!@#\$%^&*()'; print(''.join(secrets.choice(a) for _ in range($LENGTH)))" ;;
        hex)   openssl rand -hex $(( LENGTH / 2 )) ;;
        b64)   openssl rand -base64 $LENGTH | head -c $LENGTH; echo ;;
        *)     usage ;;
    esac
done
chmod +x passgen.sh
./passgen.sh -l 32 -t full -n 5

Entropy Deep Dive: Why Diceware Wins

Entropy is measured in bits. Assuming an attacker can try 1 trillion guesses/second:

Method: cat /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 12

Method: 6 EFF Diceware words

The diceware approach is superior because:

  1. Human-memorable words are easier to type correctly
  2. Distributed across word boundaries (less pattern fatigue)
  3. Mathematically equivalent to random character strings
  4. No special characters that websites reject

For user accounts: Use 5-6 EFF words (70-80 bits). For root/admin: Use 7-8 words (100+ bits).

Password Reuse: The Catastrophic Mistake

One compromised service exposes a password that works on multiple sites. This is the #1 cause of account takeover.

Consequence of password reuse:

  1. LinkedIn breach: 700 million passwords
  2. Attacker cracks a few passwords
  3. Tests those passwords on Gmail, Twitter, Banking sites
  4. Gains access to 40% of accounts

Solution: Unique password per site, generated locally.

# Store passwords in KeePassXC (encrypted local database)
# Generate passwords with KeePassXC's built-in generator
# Configure to 32 characters, all character types

# Or use a local password store with GPG encryption:
pip3 install pass
pass init your-gpg-key

# Generate and store
pass generate Banking/Checking 32
# Stores: Banking/Checking.gpg (encrypted with your GPG key)

# Retrieve
pass Banking/Checking

Never use online password managers that sync to the cloud (LastPass, 1Password cloud) if you want local control.

Master Password Strategy

If using KeePassXC or local password manager:

Master password: 8 EFF words (128 bits)
Diceware passphrase: "seven-autumn-pencil-gravity-nephew-orange-rocket-system"

Write this down and store it in a physical safe. It’s your master key to all other passwords. If you forget it, all passwords are lost forever.

Alternative: Split the master password using Shamir’s Secret Sharing

pip3 install pyminizip

# Create encrypted password database
pass init your-gpg-key

# Your master password, split into 3 shares (need 2 to recover)
# Requires external tool: https://www.ssss.readthedocs.io/
# This is advanced and rarely necessary

For most users: One strong master password in a safe location is sufficient.

Avoiding Common Password Generation Mistakes

Mistakes to avoid:

  1. Using website “random password generators”: They’re logging what they generate
  2. Using Math.random() in JavaScript: Not cryptographically secure
  3. Patterns (“Password2024!”, “Winter2025!”): Attackers specifically test these
  4. Reusing a base: “Gmail123”, “Gmail456” on multiple sites
  5. Birthdate or personal info: Easily guessable supplementary data
  6. Weak special characters: “!” “?” “@” are guessed first
  7. Keyboard patterns: “qwerty”, “12345”, “!@#$%”

Correct approach:

# Using openssl (cryptographically secure randomness)
openssl rand -base64 32 | cut -c 1-32
# Output: aB4kDpLx+8yZcQwErTuIsH/VjNmKoP

# Using /dev/urandom (pure randomness)
head -c 32 /dev/urandom | base64
# Output: x9Zm2L5vQWaE8+3pKnJbRs4MvTyU7F

Both are cryptographically secure and not guessable.

API Key Generation Strategy

For API keys and tokens (longer-lived secrets), use even stronger entropy:

# OAuth token (128 bits minimum)
openssl rand -base64 48 | cut -c 1-64

# AWS access key format (simulate)
python3 -c "
import secrets, string
# AWS format: 20 base32 chars
key = ''.join(secrets.choice(string.ascii_uppercase + '234567') for _ in range(20))
print('AKIA' + key)  # AKIA prefix for real AWS keys
"

# API key with mixed alphabet and numbers
python3 -c "
import secrets
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
api_key = 'sk_' + ''.join(secrets.choice(chars) for _ in range(48))
print(api_key)
"

Use 256-bit entropy (43 base64 characters) for API keys that authenticate applications.

Secure Password Storage After Generation

After generating a password:

  1. Copy to password manager immediately: Never store in plaintext on disk
  2. Clear clipboard after pasting: xclip -selection clipboard < /dev/null
  3. Use browser’s built-in password saver (or password manager plugin)
  4. Never email the password to yourself: Email is transmitted in plaintext

For shared team credentials (AWS accounts, SSH keys):

  1. Use a shared password manager (Bitwarden Business, 1Password Teams)
  2. Rotate credentials quarterly
  3. Audit access logs
  4. Never share via chat or email

Testing Your Generator

Verify your password generator is actually cryptographically secure:

# Generate 1000 passwords and check for patterns
for i in {1..1000}; do
    openssl rand -hex 16 | cut -c 1-16
done | sort | uniq -d | wc -l
# Should be 0 (no duplicates in 1000 samples means good randomness)

# Statistical test (NIST)
pip3 install randomness-tester
# Tests distribution of random bytes

If you see any duplicates or patterns in 1000+ samples, your generator is flawed.


Built by theluckystrike — More at zovo.one