Configuration
Configuration is the join between identical code and a specific running environment. Get it right and one build runs safely everywhere. Get it wrong and you ship a dev convenience to production or leak a secret into a config file. Keep config external, validated, environment-specific, and free of secrets.
The main idea (from the twelve-factor approach) is to separate config from code. The same artifact is promoted from dev to test to prod, and only the configuration differs. Configuration covers connection targets, feature flags, limits, and toggles. It does not cover secrets, which belong in the vault (see Secrets Management). Configuration bugs and security holes start when you mix the two, or bake environment assumptions into the build.
Configuration is also a safety surface. The Finperiti findings, RequireHttpsMetadata=false and wide-open CORS reaching production, were configuration mistakes. A setting that is fine in local dev escaped into a deployed environment. Validate config at startup. Make dev-only relaxations impossible to reach in prod. Fail fast when something required is missing.
Externalise and validate
- DoKeep configuration external to the build, so the same artifact runs in every environment and only the config differs.
- DoValidate configuration at startup. Fail fast with a clear message if something required is missing or malformed. Do not discover it at first use in production.
- DoBind config to typed, validated objects rather than reading loose strings scattered through the code.
- DoMake environment-specific differences explicit and reviewed. Build things so dev-only relaxations cannot be reached in production.
- ConsiderSafe, sensible defaults that you can override. When a value is missing, default to the secure, conservative option.
- AlwaysSource secrets from the vault via the secure workflow, never from configuration files (see Secrets at Rest & in Transit).
Change config safely
- DoVersion and review configuration changes like code. A wrong config value can take production down as surely as a bad deploy.
- DoManage feature flags with care: name them clearly, default them safely, and remove them once a feature is fully rolled out.
- ConsiderTests or a startup check that confirm security-relevant settings (HTTPS enforced, CORS scoped) are correct for the target environment.
- Do notBranch on the environment name for security behaviour (
if (env == "Dev")relaxing a control) in code that also runs in prod. Scope it so prod cannot hit the relaxed path. - Do notLet configuration drift between environments without tracking it, so prod quietly differs from what was tested.
- NeverCommit secrets, connection strings with passwords, or keys into configuration files or source control.
options.RequireHttpsMetadata = false; // "needed for local testing"
// shipped as-is; now false in production too
A setting that is reasonable on localhost disables a security check everywhere it is deployed. The build carried a dev convenience into production because the config was not environment-specific.
options.RequireHttpsMetadata = true; // default everywhere
#if DEBUG
if (env.IsDevelopment()) options.RequireHttpsMetadata = false; // local only
#endif
Production always enforces the check. The relaxation exists only in a local development build and cannot be reached in a deployed environment.
Self-review checklist
- AskIs there any secret value in configuration, rather than a vault reference?
- AskCould a dev-only relaxation in this config reach production?
- AskIf a required setting is missing, does the app fail fast at startup or break later in production?
- AskIs this config change reviewed and tracked like code?