Hardcoded credentials in Docker Compose files and application configs are a persistent source of credential exposure. GitHub's secret scanning catches some patterns — but database passwords, custom API keys, and internal service tokens are frequently missed, and the exposure often predates the scan by months.
How credentials end up exposed
The most common path: a developer creates a docker-compose.yml with hardcoded credentials for local development, adds the project to GitHub, and either forgets to add a .gitignore entry for the secrets or uses a public repo. The file sits in the repository history even after the credentials are removed.
A developer runs git add . and commits a .env file that wasn't in .gitignore. The file contains production database passwords, API keys, and JWT secrets. GitHub's secret scanning may flag some patterns, but database passwords without recognisable prefixes are not detected.
GitHub Actions workflow files with hardcoded tokens (instead of using ${{ secrets.TOKEN }}) are committed to public repositories. Build logs also sometimes echo secret values when debugging is left enabled.
What attackers do with exposed Docker credentials
Exposed database credentials in docker-compose.yml files have a predictable exploitation path:
POSTGRES_PASSWORD=mypassword in a public GitHub search.The git history problem
Removing credentials from a file and pushing the update does not remove them from git history. Anyone with access to the repository can still see the credentials in previous commits using git log -p.
# Check if credentials exist in git history: git log --all -p | grep -i "password|secret|key|token" | head -20 # Remove from history (requires git-filter-repo): pip install git-filter-repo git filter-repo --path docker-compose.yml --invert-paths
After removing from history: rotate all exposed credentials immediately. Git history removal doesn't help if the credentials were already harvested.
The correct pattern
services:
postgres:
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD} # Reference, not value
POSTGRES_USER: ${DB_USER}
DB_PASSWORD=your-actual-password DB_USER=postgres
.env .env.local .env.production *.env
Related
Paste your docker-compose.yml to detect hardcoded credentials, exposed ports, and missing security controls — nothing leaves your browser.