The getpass module provides secure password input. Characters aren't echoed to the terminal—essential for any CLI that handles credentials.

Basic Usage

Prompt for password without echo:

import getpass
 
password = getpass.getpass()
print(f"You entered {len(password)} characters")

The default prompt is "Password: " and nothing appears as you type.

Custom Prompt

import getpass
 
password = getpass.getpass(prompt='Enter your API key: ')
secret = getpass.getpass(prompt='Database password: ')

Get Username

import getpass
 
# Get current system username
username = getpass.getuser()
print(f"Logged in as: {username}")

Login Flow

Common pattern for CLI authentication:

import getpass
 
def login():
    """Prompt for credentials."""
    username = input('Username: ')
    password = getpass.getpass('Password: ')
    return username, password
 
def authenticate(username, password):
    """Verify credentials (example)."""
    # In reality, check against database/API
    return username == 'admin' and password == 'secret'
 
username, password = login()
if authenticate(username, password):
    print('Login successful!')
else:
    print('Invalid credentials')

CLI Tool Example

#!/usr/bin/env python3
import getpass
import argparse
 
def main():
    parser = argparse.ArgumentParser(description='Database CLI')
    parser.add_argument('--user', '-u', help='Username')
    parser.add_argument('--password', '-p', action='store_true',
                        help='Prompt for password')
    args = parser.parse_args()
    
    username = args.user or input('Username: ')
    
    if args.password:
        password = getpass.getpass()
    else:
        password = None
    
    connect_to_database(username, password)
 
if __name__ == '__main__':
    main()

Confirm Password

Double-entry for registration:

import getpass
 
def get_new_password():
    """Prompt for new password with confirmation."""
    while True:
        password = getpass.getpass('New password: ')
        confirm = getpass.getpass('Confirm password: ')
        
        if password == confirm:
            return password
        print("Passwords don't match. Try again.")
 
password = get_new_password()

Password Strength Check

import getpass
import re
 
def is_strong_password(password):
    """Check password meets requirements."""
    if len(password) < 8:
        return False, "Must be at least 8 characters"
    if not re.search(r'[A-Z]', password):
        return False, "Must contain uppercase letter"
    if not re.search(r'[a-z]', password):
        return False, "Must contain lowercase letter"
    if not re.search(r'\d', password):
        return False, "Must contain digit"
    return True, "OK"
 
def get_strong_password():
    """Prompt until strong password entered."""
    while True:
        password = getpass.getpass('Password: ')
        valid, message = is_strong_password(password)
        if valid:
            return password
        print(f"Weak password: {message}")

API Key Input

import getpass
 
def configure_api():
    """Prompt for API credentials."""
    print("Enter your API credentials (input is hidden)")
    api_key = getpass.getpass('API Key: ')
    api_secret = getpass.getpass('API Secret: ')
    
    return {
        'api_key': api_key,
        'api_secret': api_secret
    }

Environment Variable Fallback

import getpass
import os
 
def get_password():
    """Get password from env or prompt."""
    password = os.environ.get('APP_PASSWORD')
    if password:
        return password
    return getpass.getpass('Password: ')

SSH-Style Input

import getpass
 
def ssh_login(host):
    """SSH-style login prompt."""
    username = getpass.getuser()  # Default to current user
    
    user_input = input(f'{username}@{host}\'s password: ')
    if user_input:
        username = user_input
    
    password = getpass.getpass(f'{username}@{host}\'s password: ')
    return username, password

Handling Non-TTY

When stdin isn't a terminal (piped input):

import getpass
import sys
 
def safe_getpass(prompt='Password: '):
    """Handle both TTY and non-TTY input."""
    if sys.stdin.isatty():
        return getpass.getpass(prompt)
    else:
        # Piped input - read line (less secure)
        print(prompt, end='', file=sys.stderr)
        return sys.stdin.readline().rstrip('\n')

Timeout Pattern

import getpass
import signal
 
class TimeoutError(Exception):
    pass
 
def timeout_handler(signum, frame):
    raise TimeoutError("Input timed out")
 
def getpass_with_timeout(prompt='Password: ', timeout=30):
    """Password input with timeout."""
    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout)
    try:
        password = getpass.getpass(prompt)
        signal.alarm(0)  # Cancel alarm
        return password
    except TimeoutError:
        print("\nTimeout - no password entered")
        return None

Security Notes

getpass does:

  • Disable terminal echo
  • Read from /dev/tty when available
  • Provide basic protection from shoulder surfing

getpass doesn't:

  • Encrypt the password in memory
  • Protect against keyloggers
  • Clear password from memory after use

Clearing Sensitive Data

import getpass
import ctypes
 
def secure_input():
    """Get password and attempt to clear from memory."""
    password = getpass.getpass()
    
    # Use the password
    result = authenticate(password)
    
    # Attempt to clear (not guaranteed in Python)
    password_bytes = password.encode()
    ctypes.memset(id(password_bytes), 0, len(password_bytes))
    del password
    
    return result

Note: Python's string immutability makes true secure clearing difficult.

Custom Stream

Write prompt to different stream:

import getpass
import sys
 
# Write prompt to stderr (useful when stdout is redirected)
password = getpass.getpass(prompt='Password: ', stream=sys.stderr)

When to Use getpass

Use getpass when:

  • CLI tools need password input
  • Interactive credential prompts
  • Any secret that shouldn't be visible

Don't use when:

  • GUI applications (use proper password fields)
  • Non-interactive scripts (use env vars or config)
  • Input is piped (check sys.stdin.isatty())

Simple, focused, essential for secure CLI tools.

React to this post: