Api Key Exposure in Axum with Postgresql
Api Key Exposure in Axum with Postgresql — how this specific combination creates or exposes the vulnerability
When building a web service with Axum and Postgresql, developers commonly store database credentials and third‑party service keys in configuration files or environment variables. If these files are inadvertently included in version control, logs, error pages, or API responses, an API key exposure occurs. Axum does not expose keys by itself, but the framework’s integration patterns and the way Postgresql connection strings are handled can unintentionally surface sensitive material.
In Axum, handlers often receive a shared state that contains a database pool (e.g., using bb8_postgres or sqlx). If the state construction reads credentials from environment variables and those variables are injected from insecure sources (such as Dockerfiles, CI logs, or serverless runtime metadata), the keys can be exposed through multiple vectors:
- Error messages that include the full connection string or query parameters, revealing embedded credentials.
- OpenAPI/Swagger documentation generated from Axum route code that inadvertently echoes configuration values into examples or descriptions.
- Improper logging where the application logs the Postgresql connection details or query parameters containing API keys used for downstream services.
Because middleBrick scans the unauthenticated attack surface, it can detect API key exposure by inspecting OpenAPI specs for sensitive patterns (e.g., api_key, Authorization) and by observing runtime outputs that disclose structured error responses. For Axum applications using Postgresql, a typical risk path is: a developer places a Postgresql connection URI with an embedded API key as a query parameter (e.g., postgresql://user:api_key@host/db); if the application returns detailed SQL errors, the key is effectively leaked to any unauthenticated attacker who triggers a malformed request.
The LLM/AI Security checks in middleBrick add another dimension: they verify whether system prompts or generated outputs (for example, code suggestions or documentation) might leak API key formats or expose endpoints that handle such keys. This is particularly relevant when Axum services feed into or are augmented by LLM tooling, as prompt injection tests can surface whether keys are being carelessly echoed into model inputs or outputs.
To map findings to real-world references, middleBrick identifies issues akin to known weaknesses such as CWE-798 (Use of Hard-coded Credentials) and CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor), and aligns them with the OWASP API Top 10 and relevant compliance frameworks. For Axum+Postgresql deployments, remediation focuses on strict separation of configuration from code and robust error handling that prevents key leakage through Postgresql interactions.
Postgresql-Specific Remediation in Axum — concrete code fixes
Secure Axum applications that use Postgresql by ensuring credentials are never part of runtime data flows and by hardening how the application interacts with the database. Below are concrete, idiomatic code examples that demonstrate safe patterns.
1. Store secrets outside the application and load them safely
Use environment variables or a secrets manager, but never commit them. In Axum, build your Postgresql pool from a configuration struct that reads variables without embedding defaults that contain keys.
use std::env;
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::NoTls;
struct Settings {
db_url: String,
}
impl Settings {
pub fn from_env() -> Result<Self, &'static str> {
let db_url = env::var("DATABASE_URL").map_err(|_| "DATABASE_URL must be set")?;
Ok(Self { db_url })
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let settings = Settings::from_env()?;
let manager = PostgresConnectionManager::new(settings.db_url.parse()?, NoTls);
let pool = bb8::Pool::builder().build(manager);
// Use pool throughout your Axum handlers
Ok(())
}
2. Avoid embedding API keys in connection strings
If a third‑party API key is required for a Postgresql extension or foreign data wrapper, pass it via a dedicated secure channel rather than the connection URI. For example, use a configuration map and a custom authentication wrapper.
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::Config;
fn build_safe_config() -> Config {
let mut config = Config::new();
config.host("localhost");
config.port(5432);
config.user("app_user");
// Assume api_key is stored in a secure vault and injected as an environment variable
let api_key = std::env::var("PG_API_KEY").expect("PG_API_KEY not set");
// Use SCRAM-SHA-256 or cert-based auth instead of embedding the key in the connection string
config.password(&api_key);
config.dbname("mydb");
config
}
3. Sanitize error responses to prevent key leakage
Ensure that any database error returned by Axum does not include raw queries or connection details. Map Postgresql errors to generic messages before sending responses to the client.
use axum::{response::IntoResponse, http::StatusCode};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(#[from] sqlx::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let (status, message) = match self {
AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"[..]),
};
(status, message).into_response()
}
}
4. Validate and restrict Postgresql parameters in OpenAPI specs
If you generate OpenAPI documentation from Axum routes, avoid including sensitive parameters in examples. Use middleware or a custom extractor to scrub sensitive values before documentation is produced.
By following these patterns, Axum applications can safely integrate with Postgresql while minimizing the risk of API key exposure. middleBrick’s scans can then verify that remediation is effective, checking for exposed credentials in specs and runtime outputs.