How do you securely manage database credentials and secrets in an application?

6 minintermediatesecrets-managementcredentialssecurity

Quick Answer

Never hardcode credentials in source code or commit them to version control. Use a dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault) or, at minimum, environment variables injected at deploy time, never checked into the repository. Rotate credentials regularly and automatically where possible, use distinct credentials per environment/service (so a leak in one doesn't compromise others), and ensure the application's runtime identity, not a human developer's personal credentials, is what actually authenticates in production.

Detailed Answer

What not to do

# NEVER: hardcoded credential committed to source control
DB_PASSWORD = "SuperSecret123!"
connection = connect(host="prod-db.example.com", password=DB_PASSWORD)

Once a credential is committed to version control, it's effectively permanently compromised. Even if later removed, it typically remains in the repository's git history indefinitely unless the history itself is rewritten (a disruptive, often-incomplete remediation), and may have already been cloned, cached, or scraped by automated credential-scanning bots that actively monitor public (and sometimes private) repositories.

Better: environment variables

import os
DB_PASSWORD = os.environ["DB_PASSWORD"]   # injected by the deployment platform, not in source

An improvement — the credential isn't in source code — but environment variables can still leak via process listings, crash dumps/error logs that accidentally include the environment, or misconfigured logging that captures request/environment context.

Best: a dedicated secrets manager

import boto3
client = boto3.client("secretsmanager")
secret = client.get_secret_value(SecretId="prod/db/app-credentials")

Services like AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, or Google Secret Manager provide centralized, access-controlled storage (who/what can even retrieve a given secret is itself audited and restricted), automatic rotation (the secret's actual value can be changed on a schedule without requiring an application code deployment, as long as the application always fetches the current value rather than caching it indefinitely), and an audit trail of every access to the secret.

Additional practices

  • Distinct credentials per environment — production, staging, and development should never share a database credential. A leak of the (lower-stakes) development credential shouldn't provide any path to production data.
  • Distinct credentials per service/application, following least privilege (see that question), so a leak affecting one service's credential doesn't expose everything every other service can reach.
  • Automatic rotation wherever the tooling supports it, reducing the window of usefulness for any credential that does leak undetected.
  • Never use personal/human developer credentials for application runtime authentication. Production systems should authenticate as a dedicated service identity, both for auditability (distinguishing "the application did this" from "a specific human did this directly") and so that a developer leaving the company, or having their personal account compromised, doesn't affect production access.
  • Where supported, prefer IAM-based/managed identity authentication over static passwords entirely. Many cloud-managed databases support authenticating via the cloud platform's identity system (e.g., AWS RDS IAM authentication) rather than a long-lived static password at all, eliminating an entire class of "leaked static secret" risk.

Beyond naming a secrets manager, a strong answer recognizes the core underlying principle: secrets should never live in a place that's harder to control access to than the data they protect (source control history being the most common practical failure). It also connects credential hygiene to least privilege and rotation as complementary practices, not a single silver-bullet tool.