✦ For everyone, free.

Practical knowledge for real and everyday life

Home

14.2.2.4 Production Env Secret Risk

A focused guide to Production Env Secret Risk, connecting core concepts with practical Docker and container operations.

Production environment secret risk refers to the specific ways credentials passed as plain environment variables to a running container can leak, and why that risk is high enough in a production context that environment variables are generally considered an inadequate mechanism for anything genuinely sensitive, even though they remain a common and convenient way to pass ordinary configuration.

The inspection exposure path

Any environment variable set on a container, whether through -e, --env-file, or a Compose environment block, is fully recoverable through docker inspect:

docker inspect my-api --format '{{json .Config.Env}}'

Anyone with permission to run docker inspect against the container, which in many organizations is a broader group than those who should see a database password, can recover the value in plaintext. This is not a bug or an edge case; it is how environment variables are designed to work, and it applies regardless of how the value was originally supplied.

Inheritance by child processes

A process running inside a container automatically passes its full environment to any child process it spawns, unless the application explicitly filters it. This means a single shell invocation, debugging tool, or third-party library that spawns a subprocess can leak a secret into a log line, an error report, or a crash dump without the original application code doing anything wrong with the credential directly:

const { execSync } = require('child_process');
execSync('some-diagnostic-tool'); // inherits full process.env, including secrets

If some-diagnostic-tool happens to print its environment for debugging purposes, or a crash handler attached to it captures the environment as part of an error report, the secret ends up somewhere far outside the original application's control.

Exposure through logging and monitoring agents

Application performance monitoring and logging agents frequently capture process metadata, including environment variables, as part of their default instrumentation, especially for crash reporting:

process.on('uncaughtException', (err) => {
  errorReportingService.captureException(err, { extra: { env: process.env } });
});

A well-intentioned but careless error handler like this one ships the entire environment, secrets included, to a third-party error tracking service on every unhandled exception, creating a quiet, persistent leak that has nothing to do with the original vulnerability that triggered the exception in the first place.

Exposure through orchestrator and CI/CD tooling

Deployment tooling that prints the effective configuration for debugging purposes is a common, easily overlooked leak path:

docker compose config
deploy:
  script:
    - docker compose config  # prints resolved environment, including secrets, to job logs
    - docker compose up -d

A CI/CD pipeline step added for debugging that prints the fully resolved service definition will include any secret passed as a plain environment variable directly in the job's log output, which is often retained for a long period and accessible to a wide set of people with pipeline visibility.

Core dumps and process snapshots

A process that crashes with a core dump enabled writes its entire memory image to disk, including its environment block, at the moment of the crash:

ulimit -c unlimited

A core dump file containing a leaked secret can persist on disk indefinitely if core dump handling is not configured to either disable dumps in production or restrict and clean up the resulting files, creating a long-lived, easily overlooked exposure.

Why the risk is asymmetric in production specifically

The same environment-variable exposure paths exist in development and staging, but the consequence differs sharply: a leaked development database password affects disposable, synthetic data, while a leaked production credential can expose real customer data, financial systems, or the ability to act as the compromised service entirely. The asymmetry in consequence is the actual argument for treating production secrets with materially more care than non-production configuration, rather than applying a uniform policy across every environment.

Mitigation: moving secrets off the environment-variable path

The direct mitigation is moving sensitive values to a mounted-file or secret-manager-fetched delivery mechanism, removing them from the environment-variable exposure surface entirely:

docker run -v /run/secrets/db_password:/run/secrets/db_password:ro my-api
services:
  api:
    secrets:
      - db_password

Once a value is read from a mounted file instead of an environment variable, the inspection, inheritance, and accidental-logging risks described above either disappear or are substantially reduced, since the value no longer lives in a structure that is automatically copied, inherited, and inspected the way an environment variable is.

Auditing existing exposure

For systems already running with secrets passed as environment variables, an audit pass identifying every such case is a reasonable starting point before a broader migration to a file-based or secret-manager approach:

for c in $(docker ps --format '{{.Names}}'); do
  echo "=== $c ==="
  docker inspect "$c" --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -iE 'password|secret|token|key'
done

A pattern-based scan like this will not catch every case, since secret variable names do not always include an obviously sensitive keyword, but it surfaces the most common and most dangerous instances quickly.

Common mistakes

  • Assuming that because an environment variable is not printed anywhere by the application itself, it is therefore safe, without accounting for docker inspect, monitoring agents, and crash reporting tools that capture it independently of the application's own behavior.
  • Enabling verbose debugging output in a CI/CD pipeline without checking whether it resolves and prints secret-bearing environment variables as part of that output.
  • Applying the same secret-handling standard to production as to development, when the actual consequence of a leak differs enormously between the two.
  • Leaving core dumps enabled in production without a plan for securing or disabling the resulting files, creating a persistent, easily overlooked exposure path.
  • Treating a migration away from environment-variable secrets as optional cleanup rather than a direct reduction in production risk.

The risk in passing production secrets as environment variables is not theoretical; it comes from how many ordinary, well-intentioned tools and processes automatically capture and forward environment state, and the most effective mitigation is removing sensitive values from that exposure surface entirely rather than trying to audit every possible leak path individually.