Weak credential policy
Need
Authentication of database connections with strong credentials supplied at runtime
Context
• Usage of Rust 1.75+ for building application services
• Usage of the actix-web crate for handling HTTP requests
• Usage of the sqlx crate with MySQL for database access
Description
1. Non compliant code
use actix_web::{web, App, HttpServer};
use sqlx::mysql::{MySqlConnectOptions, MySqlPoolOptions};
async fn run() -> std::io::Result<()> {
// The reporting account has no password
let options = MySqlConnectOptions::new()
.host("analytics-host")
.username("reporting")...The `run` function below connects the service to MySQL with `MySqlConnectOptions` and sets the password of the `reporting` account to an empty string. An account with an empty password is protected only by its user name, which is usually easy to guess or visible in the source. Anyone who can reach the database port, such as another container in the cluster, a compromised host in the same network, or an attacker who exploits a server-side request forgery, can log in with that account and read or modify the data it has access to. Connection strings such as `mysql://reporting:@analytics-host/reports`, with nothing between `:` and `@`, have the same effect with `MySqlPool::connect` or Diesel `MysqlConnection::establish`. Such accounts often come from development setups that were promoted to production.
2. Steps
• Remove empty passwords from `MySqlConnectOptions::password`, `PgConnectOptions::password` and from connection strings such as `mysql://user:@host/db`.
• Load database credentials at runtime from a secrets manager or environment variables, and fail at startup when they are missing or empty.
• Assign each service its own database account with a long random password and only the privileges it needs.
• Configure the database server to reject accounts without a password and to accept connections only from the application network.
• Require TLS on database connections with `ssl_mode(...Required)` or a stricter mode.
3. Secure code example
use actix_web::{web, App, HttpServer};
use sqlx::mysql::{MySqlConnectOptions, MySqlPoolOptions, MySqlSslMode};
use std::env;
use std::io::{Error, ErrorKind};
async fn run() -> std::io::Result<()> {
// The password is injected at runtime and must not be empty
let password = env::var("DB_PASSWORD").unwrap_or_default();...The corrected function reads the password from the `DB_PASSWORD` environment variable, which the deployment injects from a secrets manager, and refuses to start when it is missing or empty. The service can no longer run with an account that has no password, and the value never appears in the source code. The connection also requires TLS with `MySqlSslMode::Required`, so the password and the data are not sent in clear text over the network. The password check in the application complements the database configuration, which should reject empty passwords on its own: the `reporting` account must be created with a long random password and granted only the privileges the service needs.
References
• 035. Weak credential policy