Sensitive information in source code - Credentials
Need
Removal of database credentials from source code and compile-time configuration
Context
• Usage of Elixir 1.15+ for building application services
• Usage of Ecto SQL with PostgreSQL for database access
• Usage of Mix releases and config/runtime.exs for configuration
Description
1. Non compliant code
import Config
config :my_app, MyApp.Repo,
hostname: "db.internal",
database: "my_app_prod",
username: "my_app",
password: "SuperSecret123!"The `config/prod.exs` file below configures the production Ecto repository with the database user name and the password `SuperSecret123!` written as literals. Configuration files are source code. The password is visible to everyone with access to the repository and stays in its history after it is removed. Because `prod.exs` is evaluated at compile time, the value is also embedded in the compiled release, so anyone who obtains the build artifact or the container image can extract it. The password cannot be rotated without a new build and deployment, and the same value tends to be reused in every environment. Credentials written as literals in `Postgrex.start_link/1`, `MyXQL.start_link/1`, `Mongo.start_link/1`, `Redix.start_link/1` or `Xandra.start_link/1` calls, or embedded in connection URLs, have the same problem.
2. Steps
• Remove passwords, API keys and connection strings with credentials from `config/*.exs` files and from `start_link` calls of Postgrex, MyXQL, Mongo, Redix and Xandra.
• Read secrets in `config/runtime.exs` with `System.fetch_env!/1`, so they are resolved when the release starts.
• Store the values in a secrets manager and inject them into the environment of the release.
• Rotate every credential that was committed, since it remains in the repository history and in built releases.
• Add a secret scanner to the CI pipeline to block new credentials in source code.
3. Secure code example
import Config
if config_env() == :prod do
config :my_app, MyApp.Repo,
hostname: System.fetch_env!("DB_HOST"),
database: System.fetch_env!("DB_NAME"),
username: System.fetch_env!("DB_USER"),
password: System.fetch_env!("DB_PASSWORD")...The corrected configuration moves the repository settings to `config/runtime.exs`, which Mix releases evaluate when the application starts, not when it is compiled. Every value is read with `System.fetch_env!/1`. The deployment platform injects the variables from a secrets manager, so the password never appears in the repository or in the release, and it can be rotated by updating the secret and restarting the application. `fetch_env!/1` raises when a variable is missing, so the release refuses to start instead of connecting with an empty or default password. The `if config_env() == :prod` guard keeps development and test configuration in their own files, which can use local credentials that grant access to nothing of value.