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) — and connects credential hygiene to least privilege and rotation as complementary practices, not a single silver-bullet tool.