Using a dedicated secrets manager is the recommended industry standard for production environments. Environment variables are common and useful in Development and Staging, but relying on them exclusively in Production can introduce security and operational risks. The goal is to separate configuration from application code, restrict access to secrets, support credential rotation, and maintain an audit trail.
Effectively, you should consider implementing secret management by environment. In Development, developers can use local .env files excluded from version control, .NET User Secrets, or a developer-oriented secret manager. Each developer should use separate credentials and a local or dedicated development database rather than Production credentials. In Staging, secrets can be injected through a CI/CD pipeline or retrieved from a secret manager, with access restricted to the staging environment. In Production, use a dedicated service such as Azure Key Vault or HashiCorp Vault. These services provide centralized access control, auditing, and support for credential rotation.
Environment variables alone may not be sufficient because while they separate secrets from source code, they do not provide comprehensive secret management. Processes running under sufficiently privileged accounts may be able to inspect them, and diagnostic dumps, crash reports, or logging tools can inadvertently capture sensitive values. Environment variables also do not inherently provide detailed auditing of who accessed a secret or automatic credential rotation. Changing a database password may require updating deployment configuration and restarting or redeploying application instances, depending on the platform and implementation.
The recommended production pattern would be to give the application a workload identity, such as an Azure Managed Identity and then grant it permission to retrieve only the secrets it requires. The application can retrieve its database connection string from the secret manager at startup or runtime and use it to establish a connection pool. Where supported, prefer short-lived credentials or identity-based database authentication over static passwords. This reduces the number of long-lived credentials that must be stored and managed. Secrets should be kept in memory where practical, never written to logs, and refreshed according to the application's credential-rotation strategy. Runtime retrieval is a strong pattern, but it is not the only secure option - platform-native secret injection can also be appropriate.
This ties to the security risks of exposing a connection string. A connection string such as postgresql://db_user:password@host:5432/mydb may reveal the database endpoint, username, password, port, and database name. If an unauthorized party obtains usable credentials and can reach the database, the consequences can include data theft, unauthorized modifications, deletion of tables or collections, ransomware, and exposure of sensitive business or customer information. A compromised database may also provide a foothold for attacking other systems if network boundaries and permissions are weak. Publicly exposed PostgreSQL and MongoDB instances are particularly vulnerable to automated scanning and opportunistic attacks.
Deleting an exposed connection string from a configuration file does not necessarily eliminate the exposure. If the value was committed to Git, it may remain in commit history, forks, clones, build artifacts, or backups. Treat any exposed credential as compromised: revoke or rotate it immediately, investigate database and application access logs, and remove the secret from repository history where appropriate. History cleanup is useful, but it does not replace credential rotation.
You should apply the principle of least privilege by creating a dedicated database account for each application and environment, rather than using a database administrator account. Grant only the permissions required by the application, such as specific read and write operations on designated tables or collections. Enforce TLS for database connections, restrict network access to trusted application environments, and avoid logging complete connection strings. Use automated secret-scanning tools, such as GitHub Secret Scanning to detect exposed credentials in repositories and CI/CD workflows. Where supported, use short-lived, identity-based credentials instead of permanent passwords.
If the above response helps answer your question, remember to "Accept Answer" so that others in the community facing similar issues can easily find the solution. Your contribution is highly appreciated.
hth
Marcin