skip to content

Is an AWS Lambda environment variable a safe place to store a database password? Explain how Lambda protects environment variables and who is able to read them.

level: middleimportance: should knowfreq 52%

answer

  1. encrypted at rest is not the question
  2. who may read the configuration is
  3. default managed key hides nothing
  4. a customer managed key adds a decrypt check
  5. static config versus a rotating credential

basics

~20 s

Not for a real secret. Lambda encrypts environment variables at rest with KMS, but anyone allowed to read the function's configuration sees the decrypted value, and the value is static, so a rotated password silently goes stale.

solid answer

~50 s

Lambda encrypts environment variables at rest using KMS — by default the AWS managed `aws/lambda` key — and injects them into the execution environment as plaintext for your code. The gap is on the read path: with the default key, any principal allowed `lambda:GetFunctionConfiguration` gets the values back in clear text, and that permission is usually handed out as harmless read-only access. Choosing a customer managed key narrows that, because reading the configuration then also requires `kms:Decrypt` on your key, which you can restrict by key policy. The second problem is lifecycle: an environment variable is a static piece of function configuration, so rotating the password means redeploying every function that holds it, and until then the old value is still in use. Use environment variables for non-secret configuration — table names, endpoints, log level — and keep credentials in Secrets Manager or Parameter Store, fetched at runtime.

code

bash · 7 lines
bash
aws lambda get-function-configuration \
  --function-name order-writer \
  --query 'Environment.Variables'

aws lambda update-function-configuration \
  --function-name order-writer \
  --kms-key-arn arn:aws:kms:eu-west-1:111122223333:key/1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d

go deeper

for a junior

Know that environment variables are for configuration such as table names and log level, and that a password belongs in Secrets Manager or Parameter Store instead. Say why in one sentence: the value is readable and static.

for a middle

Explain the mechanism — KMS encryption at rest with the AWS managed key by default, plaintext returned to any principal that can read the function configuration, and a 4 KB total size cap.

for a senior

Show the control design: a customer managed key so decryption is an auditable, policy-controlled grant, secret identifiers rather than secrets in configuration, and a rotation story that does not require redeploying functions.

for a principal

Own the standard across the estate — where credentials may live at all, how key policies and configuration-read permissions are governed together, and the cost and blast-radius tradeoff between per-team keys and a shared one.

## What Lambda actually does with environment variables Environment variables are part of a function's configuration. You set them with `UpdateFunctionConfiguration`, they are stored by the Lambda service encrypted at rest with a KMS key, and at the start of each execution environment Lambda decrypts them and places them in the process environment, where your runtime reads them as ordinary variables (`process.env.X`, `os.environ["X"]`). The key used for that encryption is the AWS managed key `aws/lambda` unless you specify your own through the function's `KMSKeyArn`. This distinction is the whole security story, because it decides who can read the values back. ## The read path is the risk, not the storage "Encrypted at rest" says nothing about who is authorised to decrypt. With the default AWS managed key, the Lambda service will decrypt on behalf of anyone permitted to describe the function, so a single API call returns the values: ```bash aws lambda get-function-configuration \ --function-name order-writer \ --query 'Environment.Variables' # { "DB_PASSWORD": "hunter2", "TABLE_NAME": "orders" } ``` `lambda:GetFunctionConfiguration` — along with `lambda:GetFunction` and the broad read-only managed policies that include them — is exactly the permission organisations hand to auditors, support engineers, dashboards and CI tooling on the assumption that reading configuration is innocuous. It is not, once a credential is in there. The console shows the same values in the function's configuration pane. Assigning a **customer managed key** changes the calculus. The values are then encrypted under a key whose policy you write, and reading the configuration requires `kms:Decrypt` on that key in addition to the Lambda read permission. Now "who can see the password" is a question you can answer and enforce, and every decryption is a CloudTrail event on your key. A stricter variant stores ciphertext in the variable and decrypts it in the handler, so the Lambda API never returns plaintext at all — but at that point you have hand-built a worse version of a secrets store. ## The lifecycle problem Even with a customer managed key, environment variables are the wrong home for a rotating credential. Values are frozen into function configuration: - Rotating the password requires an `UpdateFunctionConfiguration` on every function that carries it, coordinated with the rotation itself. Between the two, callers authenticate with the wrong value. - The value tends to leak into places you did not intend: infrastructure templates checked into git, pipeline variables, deployment logs, screenshots of the console. - Environment variables are also capped — the total size of a function's environment variables is 4 KB as of 2026 — so a certificate or a large credential bundle does not fit anyway. Secrets Manager and Parameter Store exist for precisely this shape: the value is fetched at runtime, so rotating it changes behaviour without touching the function; access is a separate IAM decision (`secretsmanager:GetSecretValue`, `ssm:GetParameter`, plus `kms:Decrypt` for a SecureString or a customer managed key); and every read is auditable. ## The pragmatic split A rule of thumb that survives interview scrutiny: - **Environment variables** — non-secret configuration that differs per stage: table and bucket names, queue URLs, endpoint hostnames, log level, feature flags, the *identifier* of the secret to fetch. These also cost nothing to read and are available instantly. - **Secrets Manager / Parameter Store** — anything that would be damaging to disclose or that rotates: database passwords, third-party API keys, signing keys, private certificates. Notice that the environment variable still plays a role in the second case: you put the secret's name or ARN in `DB_SECRET_ID` and let the code resolve it. That keeps the function's configuration environment-specific without ever carrying the credential. ## Answering the question well Do not answer with a flat "no, that's insecure" — interviewers want the mechanism. Say that the values are KMS-encrypted at rest but returned decrypted to anyone with configuration-read permission under the default key; that a customer managed key turns that into an explicit, auditable `kms:Decrypt` grant; and that even then, static configuration is a poor home for a value that rotates. Then give the split: identifiers in environment variables, credentials in a secrets store.

  • What does specifying a customer managed KMS key for a function's environment variables change?
    It moves the decision into a key policy you control. Values are encrypted under your key, so reading them through the Lambda API or console also requires `kms:Decrypt` on that key — configuration-read permission alone is no longer enough. Every decryption is recorded in CloudTrail against your key, which gives you both a chokepoint and an audit trail that the AWS managed key does not.
  • So what should go in environment variables?
    Configuration that is environment-specific but not sensitive: bucket and table names, queue URLs, endpoint hostnames, log level, feature flags, and the identifier or ARN of the secret the function should fetch. That last one is the useful pattern — the variable tells the function which credential to resolve at runtime, while the credential itself never appears in the function's configuration.
  • Someone says the password is fine in an environment variable because the function is private. Why is that reasoning weak?
    It conflates network reachability with data disclosure. Nobody has to invoke the function to see the value — a single `GetFunctionConfiguration` call returns it, and that permission is routinely granted to auditors, tooling and read-only roles that have no business holding a database credential. Privacy of the invoke path does nothing about the control-plane read path.

saying these in an interview costs you the question

  • Says encrypted at rest means nobody can read the value
  • Believes the value is only visible inside the handler
  • Treats lambda:GetFunctionConfiguration as harmless read-only access
  • Leaves a rotating password in configuration and never updates it
  • Thinks a customer managed key changes nothing about who can read

context