Claude Code for getpass: Python Secure Password Input — Claude Skills 360 Blog
Blog / AI / Claude Code for getpass: Python Secure Password Input
AI

Claude Code for getpass: Python Secure Password Input

Published: November 22, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s getpass module reads passwords from the terminal with echo suppressed and retrieves the current username. import getpass. Password prompt: password = getpass.getpass(prompt="Password: ", stream=None) — disables terminal echo, reads one line, restores echo; stream defaults to the controlling tty or sys.stderr (not stdout, to avoid contaminating piped output). Username: getpass.getuser() → string — checks LOGNAME, USER, LNAME, USERNAME environment variables in order, then falls back to pwd.getpwuid(os.getuid()).pw_name on Unix. Warning: getpass.GetPassWarning — subclass of UserWarning emitted when the terminal doesn’t support echo suppression (piped stdin, some CI environments, IDEs); in those cases the password is still read but will be visible. Platform notes: on Windows uses msvcrt.getwch(); on Unix uses termios; always reads from /dev/tty if available to bypass stdin redirection. Claude Code generates terminal authentication prompts, interactive credential collectors, sudo-style privilege escalation helpers, and CLI login flows.

CLAUDE.md for getpass

## getpass Stack
- Stdlib: import getpass
- Password: pw = getpass.getpass("Password: ")     # echo disabled
-            pw = getpass.getpass(stream=sys.stderr) # explicit stream
- User:    name = getpass.getuser()                 # current login name
- Warning: GetPassWarning  — echo may be visible (pipe/IDE)
- Note:    Never log or serialize the returned string; hash it immediately

getpass Secure Password Input Pipeline

# app/getpassutil.py — prompt, hash, verify, retry, sudo-style privilege check
from __future__ import annotations

import getpass
import hashlib
import hmac
import os
import secrets
import sys
import warnings
from dataclasses import dataclass


# ─────────────────────────────────────────────────────────────────────────────
# 1. Secure prompt helpers
# ─────────────────────────────────────────────────────────────────────────────

def prompt_password(
    prompt: str = "Password: ",
    confirm: bool = False,
    confirm_prompt: str = "Confirm password: ",
    stream=None,
) -> str:
    """
    Prompt for a password with optional confirmation.
    Returns the entered password string.
    Raises ValueError if confirmation doesn't match.

    Example:
        pw = prompt_password("New password: ", confirm=True)
    """
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", getpass.GetPassWarning)
        pw = getpass.getpass(prompt, stream=stream)
        if confirm:
            pw2 = getpass.getpass(confirm_prompt, stream=stream)
            if pw != pw2:
                raise ValueError("Passwords do not match")
    return pw


def prompt_credentials(
    username_prompt: str = "Username: ",
    password_prompt: str = "Password: ",
) -> tuple[str, str]:
    """
    Prompt for username (visible) and password (hidden).
    Returns (username, password).

    Example:
        user, pw = prompt_credentials()
    """
    username = input(username_prompt).strip()
    password = prompt_password(password_prompt)
    return username, password


# ─────────────────────────────────────────────────────────────────────────────
# 2. Password hashing (for storage)
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class HashedPassword:
    """
    PBKDF2-HMAC-SHA256 hashed password suitable for storage.
    """
    algorithm: str        # "pbkdf2_sha256"
    iterations: int
    salt_hex:  str        # 32 hex chars = 16 bytes
    hash_hex:  str        # 64 hex chars = 32 bytes

    def to_string(self) -> str:
        """Return portable string: algorithm$iterations$salt$hash"""
        return f"{self.algorithm}${self.iterations}${self.salt_hex}${self.hash_hex}"

    @classmethod
    def from_string(cls, s: str) -> "HashedPassword":
        parts = s.split("$")
        if len(parts) != 4:
            raise ValueError(f"Invalid hashed password format: {s!r}")
        return cls(
            algorithm=parts[0],
            iterations=int(parts[1]),
            salt_hex=parts[2],
            hash_hex=parts[3],
        )


def hash_password(
    password: str,
    iterations: int = 260_000,
) -> HashedPassword:
    """
    Hash a plaintext password using PBKDF2-HMAC-SHA256.
    Returns a HashedPassword for storage.

    Example:
        hashed = hash_password("s3cr3t")
        stored = hashed.to_string()
        # later:
        if verify_password("s3cr3t", HashedPassword.from_string(stored)):
            print("authenticated")
    """
    salt = secrets.token_bytes(16)
    dk = hashlib.pbkdf2_hmac(
        "sha256",
        password.encode("utf-8"),
        salt,
        iterations,
        dklen=32,
    )
    return HashedPassword(
        algorithm="pbkdf2_sha256",
        iterations=iterations,
        salt_hex=salt.hex(),
        hash_hex=dk.hex(),
    )


def verify_password(password: str, hashed: HashedPassword) -> bool:
    """
    Verify a plaintext password against a HashedPassword.
    Uses constant-time comparison to prevent timing attacks.

    Example:
        ok = verify_password(entered_pw, stored_hashed)
    """
    salt = bytes.fromhex(hashed.salt_hex)
    dk = hashlib.pbkdf2_hmac(
        "sha256",
        password.encode("utf-8"),
        salt,
        hashed.iterations,
        dklen=32,
    )
    return hmac.compare_digest(dk.hex(), hashed.hash_hex)


# ─────────────────────────────────────────────────────────────────────────────
# 3. Retry logic
# ─────────────────────────────────────────────────────────────────────────────

def authenticate_with_retry(
    verify_fn: "callable[[str, str], bool]",
    max_attempts: int = 3,
    username: str | None = None,
) -> bool:
    """
    Prompt for credentials up to max_attempts times, calling verify_fn(user, pw).
    Returns True on success, False after exhausting attempts.

    Example:
        PASSWORD_DB = {"alice": hash_password("s3cr3t")}

        def check(user, pw):
            h = PASSWORD_DB.get(user)
            return h is not None and verify_password(pw, h)

        if authenticate_with_retry(check):
            print("Welcome!")
        else:
            print("Too many failed attempts")
    """
    for attempt in range(1, max_attempts + 1):
        try:
            if username is None:
                user = input("Username: ").strip()
            else:
                user = username
            pw = prompt_password()
        except (EOFError, KeyboardInterrupt):
            print()
            return False
        if verify_fn(user, pw):
            return True
        remaining = max_attempts - attempt
        if remaining > 0:
            print(f"Incorrect. {remaining} attempt{'s' if remaining != 1 else ''} remaining.",
                  file=sys.stderr)
        else:
            print("Authentication failed.", file=sys.stderr)
    return False


# ─────────────────────────────────────────────────────────────────────────────
# 4. Privilege escalation helper (sudo-style)
# ─────────────────────────────────────────────────────────────────────────────

def current_user() -> str:
    """
    Return the login name of the current process owner.
    Uses getpass.getuser() which checks env vars then pwd database.

    Example:
        print(f"Running as: {current_user()}")
    """
    return getpass.getuser()


def is_root() -> bool:
    """Return True if the current process is running as root (uid 0) on Unix."""
    return os.getuid() == 0 if hasattr(os, "getuid") else False


@dataclass
class ElevationResult:
    authorized: bool
    user: str
    message: str

    def __str__(self) -> str:
        status = "AUTHORIZED" if self.authorized else "DENIED"
        return f"[{status}] {self.user}: {self.message}"


def require_password_confirmation(
    prompt: str = "Confirm your password to continue: ",
    stored_hash_str: str | None = None,
) -> ElevationResult:
    """
    Ask the user to confirm their own password before a sensitive operation.
    If stored_hash_str is provided, verifies against it.
    If None, always returns authorized=True after entry (trust-on-entry mode).

    Example:
        result = require_password_confirmation(
            stored_hash_str=my_stored_hash.to_string()
        )
        if not result.authorized:
            sys.exit(1)
    """
    user = current_user()
    try:
        pw = prompt_password(prompt)
    except (EOFError, KeyboardInterrupt):
        print()
        return ElevationResult(False, user, "Cancelled by user")

    if stored_hash_str is None:
        return ElevationResult(True, user, "Password accepted (trust mode)")

    hashed = HashedPassword.from_string(stored_hash_str)
    if verify_password(pw, hashed):
        return ElevationResult(True, user, "Password verified")
    return ElevationResult(False, user, "Incorrect password")


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== getpass demo ===")

    # ── current_user ──────────────────────────────────────────────────────────
    print(f"\n--- current_user ---")
    print(f"  getpass.getuser(): {getpass.getuser()!r}")
    print(f"  is_root(): {is_root()}")

    # ── hash_password / verify_password ───────────────────────────────────────
    print(f"\n--- hash_password / verify_password ---")
    hashed = hash_password("correct_horse_battery_staple", iterations=10_000)
    stored = hashed.to_string()
    print(f"  stored: {stored[:60]}...")
    print(f"  verify correct:   {verify_password('correct_horse_battery_staple', hashed)}")
    print(f"  verify incorrect: {verify_password('wrong_password', hashed)}")

    # ── HashedPassword round-trip ─────────────────────────────────────────────
    print(f"\n--- HashedPassword round-trip ---")
    restored = HashedPassword.from_string(stored)
    print(f"  algorithm={restored.algorithm}  iterations={restored.iterations}")
    print(f"  restored verify: {verify_password('correct_horse_battery_staple', restored)}")

    # ── Non-interactive prompt simulation ─────────────────────────────────────
    print(f"\n--- getpass.getpass in non-interactive context ---")
    print("  (skipping live prompt in demo — use in interactive terminal)")
    print("  pw = getpass.getpass('Password: ')  # echo disabled")
    print("  user, pw = prompt_credentials()     # username + password")

    # ── GetPassWarning example ────────────────────────────────────────────────
    print(f"\n--- GetPassWarning detection ---")
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        # In a redirected environment, getpass emits GetPassWarning
        # We simulate the warning detection pattern:
        had_warning = any(issubclass(x.category, getpass.GetPassWarning) for x in w)
        print(f"  GetPassWarning caught: {had_warning}")

    print("\n=== done ===")

For the pwinput (PyPI) alternative — pwinput.pwinput(prompt, mask="*") shows masking characters as the user types (asterisks or any char) rather than complete silence — use pwinput when visual feedback that keystrokes are being received is important for UX; use getpass.getpass (showing nothing) for highest security where even asterisk count revelation is undesirable, or in headless/automated environments where pwinput would behave unhelpfully. For the keyring (PyPI) alternative — keyring.get_password(service, username) and keyring.set_password(service, username, password) store and retrieve credentials in the OS native secret store (macOS Keychain, Windows Credential Locker, libsecret on Linux) — use keyring for applications that need to persist credentials between runs so the user doesn’t have to retype; use getpass for one-shot interactive prompts where credentials should not be stored on disk, or for first-time password input before hashing and storing a derived key. The Claude Skills 360 bundle includes getpass skill sets covering prompt_password()/prompt_credentials() with echo suppression and confirmation, HashedPassword with hash_password()/verify_password() PBKDF2-HMAC-SHA256 storage, authenticate_with_retry() multi-attempt login loop, and ElevationResult with require_password_confirmation() sudo-style privilege helper. Start with the free tier to try secure input patterns and getpass pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free