Bandit's Hardcoded Password Detection: Rules B105-B107 in Practice

Bandit's Hardcoded Password Detection: Rules B105-B107 in Practice

Picture this: you’re reviewing a pull request at 2 AM, trying to ship a critical bug fix before morning. Hidden in line 247 of a configuration file, you find DATABASE_PASSWORD = "prod_db_2023!". Your heart sinks as you realize this password has been sitting in your GitHub repository for three months, visible to anyone with read access.

This scenario plays out more often than you’d think. Hardcoded passwords are like digital land mines-they seem harmless until they explode in your face. Bandit’s rules B105, B106, and B107 exist specifically to catch these ticking time bombs before they make it to production.

The Anatomy of a Security Disaster

Hardcoded passwords create a cascade of security problems that extends far beyond the immediate code. When you embed a password directly in your source code, you’re not just making it visible to anyone who can read the file-you’re creating a permanent record that’s nearly impossible to erase.

Version control systems like Git remember everything. Even if you remove the password in a later commit, it remains in your repository’s history. Security teams have horror stories of developers who accidentally committed AWS keys to public repositories, only to discover cryptocurrency miners had found and exploited those keys within hours.

How Bandit Hunts for Hidden Secrets

Bandit’s password detection is surprisingly sophisticated. It doesn’t just look for the literal word “password”-it understands the patterns and conventions developers use when naming variables that store sensitive information.

# B105: Hardcoded password string
password = "admin123"  # Flagged

# B106: Hardcoded password in function argument  
connect_db(host="localhost", password="secret")  # Flagged

# B107: Hardcoded password as default parameter
def login(username, password="default_pwd"):  # Flagged
    pass

Rule B105 catches the most obvious cases: variables with names like password, secret, token, or api_key that are assigned string literals. Rule B106 focuses on function arguments where secrets get passed around. Rule B107 catches default parameter values in function definitions, which is particularly insidious because default passwords often start as placeholder values during development but somehow survive into production code.

The Psychology of Hardcoded Passwords

Why do smart developers keep making this mistake? Usually, it starts innocently enough. You’re prototyping a feature and need to connect to a database quickly. Rather than setting up proper configuration, you hardcode the development password “just temporarily.”

The temporary solution works, so it stays. Weeks later, when you’re rushing to deploy, you forget to replace the hardcoded value with proper configuration. Before you know it, your development password is running in production.

Building Better Habits

The solution to hardcoded passwords isn’t just technical-it’s about building development habits that make secure coding the path of least resistance. Modern applications should treat configuration and secrets as external dependencies that get injected at runtime.

# Secure approach - use environment variables
import os
password = os.environ.get('DATABASE_PASSWORD')
api_key = os.environ.get('API_KEY')

# Or use a secrets management service
from your_secrets_lib import get_secret
password = get_secret('database_password')

Environment variables are the most common approach, allowing the same code to run in different environments with different credentials. For teams serious about security, dedicated secrets management services offer features like automatic rotation, access logging, and fine-grained permissions.

Learning from False Positives

Bandit’s password detection occasionally flags legitimate code that isn’t actually a security issue. Test data, example configurations, and obviously fake placeholder values sometimes trigger alerts. When this happens, it’s an opportunity to make your code’s intent clearer.

Instead of using # nosec comments to silence warnings, consider whether you can restructure the code to be more obviously safe. Use variable names like example_password or placeholder_secret that clearly indicate non-sensitive values.

Making Security Sustainable

The goal isn’t to eliminate every possible security risk-it’s to build systems that are secure by default and fail safely when things go wrong. Bandit’s password detection rules help establish that foundation by catching one of the most common and preventable security mistakes.

When you see B105, B106, or B107 in a Bandit report, don’t just fix the immediate issue. Take a moment to understand why the hardcoded value existed in the first place and what you can do to prevent similar issues in the future. Sometimes the best fix involves changing not just the code, but the development process that allowed the vulnerable code to be written.

Building secure applications is a team sport. Bandit’s rules provide the safety net, but lasting security comes from developers who understand why these rules exist and how to write code that doesn’t need them.

Subscribe to the Newsletter

Get the latest posts and insights delivered straight to your inbox.