Skip to content

Securing Credentials and Environment Variables

Securing Credentials and Environment Variables

Section titled “Securing Credentials and Environment Variables”

Credentials and environment variables are common compromise paths in modern delivery systems. Most incidents are not caused by crypto weaknesses. They come from mis-scoped tokens, leaked logs, overprivileged CI jobs, and long-lived secrets.

This guide provides practical controls for CI/CD pipelines and runtime systems.

Primary attack paths:

  1. Secrets committed to source control.
  2. Secrets exposed in build logs and artifacts.
  3. Stolen CI runner tokens with broad privileges.
  4. Lateral movement via shared environment variables.
  5. Compromised dependency or pipeline step exfiltrating secrets.

High-value targets:

  1. Cloud API keys and workload credentials.
  2. Artifact signing keys.
  3. Registry and deployment tokens.
  4. Database credentials.
  1. Use short-lived credentials whenever possible.
  2. Scope credentials to least privilege and shortest lifetime.
  3. Inject credentials at runtime, never at build authoring time.
  4. Keep secrets out of source, logs, and build outputs.
  5. Make all sensitive operations auditable.
  1. Static long-lived keys:
    • Highest risk.
    • Use only as temporary migration fallback.
  2. Rotating service tokens:
    • Better than static keys.
    • Still risky if broadly scoped.
  3. Dynamic credentials from secret brokers (Vault, cloud STS):
    • Preferred.
    • Time-bound and revocable.
  4. Federated identity (OIDC/workload identity):
    • Strongest default for CI-to-cloud auth.
    • Removes many stored secret cases.

Environment variables are useful but easy to leak.

Controls:

  1. Do not place secrets in global job-level environment blocks.
  2. Inject secret env vars only in the narrowest stage/step scope.
  3. Disable command echo and shell tracing in sensitive steps.
  4. Mask high-risk patterns in logs.
  5. Separate non-sensitive config from secrets.

Bad pattern:

Terminal window
export AWS_SECRET_ACCESS_KEY="..."
set -x
./deploy.sh

Safer pattern:

Terminal window
set +x
aws sts assume-role --role-arn "$ROLE_ARN" --role-session-name "ci" > /tmp/creds.json
export AWS_ACCESS_KEY_ID="$(jq -r .Credentials.AccessKeyId /tmp/creds.json)"
export AWS_SECRET_ACCESS_KEY="$(jq -r .Credentials.SecretAccessKey /tmp/creds.json)"
export AWS_SESSION_TOKEN="$(jq -r .Credentials.SessionToken /tmp/creds.json)"
./deploy.sh
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

Recommended pipeline control flow:

  1. Authenticate workload identity.
  2. Exchange for short-lived token.
  3. Fetch scoped secret from broker.
  4. Use secret in one isolated step.
  5. Clear environment and workspace sensitive files.
  6. Emit redacted audit metadata only.

Design rule:

  1. No secret should survive beyond the stage that needs it.
  1. Use withCredentials for short-lived scoped exposure.
  2. Avoid storing secrets in global node environment configuration.
  3. Restrict pipeline script approvals and shared library update rights.
  4. Run agents as ephemeral workers, not long-lived mutable hosts.
  5. Deny outbound egress from jobs that do not require internet access.

Example:

pipeline {
agent any
stages {
stage('Deploy') {
steps {
withCredentials([string(credentialsId: 'deploy-token', variable: 'DEPLOY_TOKEN')]) {
sh '''
set +x
./deploy.sh
'''
}
}
}
}
}
  1. Prefer OIDC federation to cloud providers over static repository secrets.
  2. Set permissions minimally per workflow/job.
  3. Use environment protection rules for production credentials.
  4. Prevent untrusted forks from accessing protected secrets.
  5. Pin third-party actions by immutable commit SHA.

Example:

permissions:
id-token: write
contents: read
packages: write
  1. Store secrets in dedicated secret manager integration where possible.
  2. Limit secret access with namespace and service account boundaries.
  3. Mount secrets only into pods that need them.
  4. Rotate secrets and trigger rolling restarts safely.
  5. Avoid passing high-value credentials via plain env vars for long-lived pods when file mounts or broker fetch is possible.

Rotation policy:

  1. Human credentials: rapid expiration plus MFA.
  2. Machine credentials: automatic rotation on short schedule.
  3. Emergency revocation: immediate disable path with runbook.

Operational requirements:

  1. Track secret age and last-used timestamp.
  2. Alert on stale or unused privileged credentials.
  3. Validate rotation through synthetic pipeline tests.
  1. Use log redaction filters in CI and runtime logs.
  2. Scan commits and artifacts for secret patterns.
  3. Alert on suspicious token use geography/time anomalies.
  4. Retain immutable audit logs for credential issuance and use.
  5. Correlate pipeline run IDs to cloud API calls for forensic traceability.
  1. Policy as code gates on secret usage patterns.
  2. Enforce mandatory rotation intervals for high-privilege credentials.
  3. Block pipeline merges that introduce plaintext secrets.
  4. Require peer review for changes touching auth and secret handling.
  5. Periodically run break-glass tabletop exercises.
  1. Remove static cloud keys from CI and move to OIDC or dynamic tokens.
  2. Scope all secret injection to stage-level, not pipeline-level.
  3. Disable shell tracing in secret-handling steps.
  4. Implement secret scanning in pre-commit and CI.
  5. Add automated rotation and immediate revocation runbooks.
  6. Audit all privileged token scopes and reduce blast radius.
  7. Verify no sensitive env vars are exported into artifacts.

Credential security is mostly a lifecycle and blast-radius problem. If you adopt short-lived identity, scoped injection, strict logging hygiene, and enforceable policy gates, you can reduce compromise risk substantially without slowing delivery.