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.

© 2026 Xygeni. All rights reserved