Database Connection URI Password
ID |
db_connection_uri |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
high |
Remediation Effort |
medium |
Family |
Data Storage Secret |
Description
Most database drivers accept a single URI carrying every connection detail, with the credentials in the authority component: scheme://user:password@host:port/database.
postgresql:// is the form libpq, SQLAlchemy and Django read from DATABASE_URL, and it is the twelve-factor default that Heroku-style platforms hand to an application. mysql:// plays the same role for the MySQL clients. The shape is shared by MariaDB, SQL Server, ClickHouse and CockroachDB.
This detector reports the password held in that position. The sibling db_connection_string detector covers the semicolon-delimited key=value DSN and the jdbc:…?password= form instead, and MongoDB, Redis and AMQP each carry a detector of their own.
Security
A URI of this kind is a complete credential: host, database, user and password in one string. Anyone who reads it can connect as that user, so leaking one in .env, settings.py, a compose file or a CI variable exposes the database and everything reachable from it.
The password is often percent-encoded, which hides nothing — T7%24hQz2LmVx9 is T7$hQz2LmVx9 to any client that parses the URI.
Examples
DATABASE_URL=postgresql://report_rw:T7%24hQz2LmVx9@db-primary.internal:5432/reporting
mysql://appuser:Kp4wNc8RtBz1@db.internal:3306/app
Mitigation / Fix
Rotate the password: once the URI has been committed, it has to be treated as known.
Then keep the credential out of the string, reading it from the environment or a secret manager at the point the URI is built:
DATABASE_URL=postgresql://report_rw:${DB_PASSWORD}@db-primary.internal:5432/reporting
A reference of that form is not reported, and neither is a URI pointing at localhost or 127.0.0.1, which is a local development default rather than a credential worth protecting.