Zero Trust Begins with Zero Shared Secrets: Modern Credential Management

· Emma Rodriguez · 8 min read

Every zero trust architecture document emphasizes "never trust, always verify." Yet the same organizations that implement identity-aware proxies and microsegmentation still deploy applications with hardcoded database passwords, long-lived API keys shared across teams, and TLS certificates that expire once a year. These shared secrets create implicit trust that bypasses every zero trust control you have built. When a single environment variable leaks through a log aggregation pipeline or a misconfigured debug endpoint, the attacker inherits the full permissions of that credential without triggering any identity verification. This article examines why shared secrets are incompatible with zero trust, and presents the infrastructure patterns that eliminate them: ephemeral credentials, workload identity, secret vaults with dynamic credential generation, and CDN edge credential injection.

The Shared Secret Problem

Shared secrets fail zero trust for three fundamental reasons. First, they are bearer credentials: anyone who possesses the secret is granted access regardless of identity, location, or device posture. A database password does not know whether it is being used by the production application or by an attacker's script running from a compromised developer laptop. Second, they persist indefinitely unless explicitly rotated. A leaked secret remains valid until someone discovers the leak and rotates it, which according to industry data takes an average of 277 days. Third, they resist rotation because they are shared: changing a database password requires coordinated updates across every service that uses it, creating a synchronization problem that encourages teams to delay rotation.

# The shared secret anti-pattern: static credentials everywhere
# .env file committed to repo (or leaked via /debug endpoint)
DATABASE_URL=postgres://app_user:P@[email protected]:5432/production
REDIS_PASSWORD=redis-secret-key-shared-across-12-services
STRIPE_API_KEY=sk_live_51H7...  # Same key used in dev and prod
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

# Blast radius of a single .env leak:
# - Full read/write to production database
# - Access to all cached session data
# - Ability to issue charges via payment processor
# - Full AWS API access for the configured IAM user

The infrastructure cost of shared secrets compounds over time. Security teams maintain spreadsheets of which services use which credentials. Rotation requires coordinated deployments across multiple services. Incident response for a leaked credential involves tracing every system that might have been accessed, because the credential provides no audit trail of who used it or when.

Ephemeral Credentials: The Foundation

Ephemeral credentials replace static secrets with short-lived, automatically generated credentials that expire within minutes. The workload authenticates to a credential broker using its platform identity (not a password), receives a freshly generated credential with a defined TTL, uses it for the duration of the TTL, and requests a new one before expiration. No credential is ever stored on disk, committed to version control, or shared between services.

Performance Comparison: Edge vs Origin Processing
Metric Edge Processing Origin Processing Improvement
Latency (p50)8–15 ms120–300 ms15–20x faster
Cache Hit Ratio85–95%N/AReduces origin load
TLS Handshake1–5 ms (nearby PoP)50–150 ms10–30x faster
Bandwidth Cost$0.01–0.04/GB$0.05–0.12/GB3–5x cheaper
Availability99.99% (multi-PoP)99.9% (single DC)10x fewer outages
Ephemeral Credential Lifecycle with Vault and SPIFFE Ephemeral Credential Lifecycle Workload payment-service K8s pod identity SPIRE Agent Issues SVID (X.509) TTL: 1 hour HashiCorp Vault Dynamic Secrets Engine Authenticates via SPIFFE Generates ephemeral creds PostgreSQL Creates temp user TTL: 30 min Credential Lifecycle Timeline Create Active (30 min TTL) Renew Expire + Revoke Blast Radius Comparison: Static vs Ephemeral Credential Leak Static Credential Leaked Valid indefinitely until manual rotation Avg detection: 277 days | Full DB access Ephemeral Credential Leaked Auto-expires in ≤30 minutes Max exposure: 30 min | Scoped permissions 1. Attest 2. Auth via SVID 3. Create temp user 4. Return ephemeral credentials
Figure 1: The ephemeral credential lifecycle. The workload never handles persistent secrets. SPIFFE provides identity, Vault generates short-lived database credentials, and automatic expiration limits blast radius.

HashiCorp Vault dynamic secrets

Vault's dynamic secrets engine generates unique credentials on demand for each requesting workload. When a service needs database access, Vault creates a temporary database user with specific permissions and a TTL. When the TTL expires, Vault automatically revokes the user. No two service instances share credentials, and every credential has a bounded lifetime.

# Vault dynamic database credential configuration
vault write database/config/production \
    plugin_name=postgresql-database-plugin \
    connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/prod" \
    allowed_roles="payment-readonly,payment-readwrite" \
    username="vault-admin" \
    password="initial-setup-only"

# Role definition: what the generated credentials can do
vault write database/roles/payment-readonly \
    db_name=production \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' \
      VALID UNTIL '{{expiration}}'; \
      GRANT SELECT ON orders, payments, refunds TO \"{{name}}\";" \
    revocation_statements="DROP ROLE IF EXISTS \"{{name}}\";" \
    default_ttl="30m" \
    max_ttl="1h"

# Service requests credentials at runtime (authenticated via SPIFFE)
vault read database/creds/payment-readonly
# Returns:
# username: v-spiffe-payment-readonly-abc123
# password: A1b2C3d4E5f6G7h8
# lease_duration: 30m
# lease_id: database/creds/payment-readonly/lease-xyz

The generated username includes the requesting identity and role, providing immediate attribution in database audit logs. When investigating suspicious queries, the database log shows exactly which service instance executed each statement, without any additional instrumentation.

SPIFFE: Workload Identity Without Secrets

SPIFFE eliminates the bootstrap problem of "how does the workload prove its identity to get its first credential?" by using platform attestation instead of shared secrets. The SPIRE agent on each node verifies the workload's identity through the infrastructure layer (Kubernetes API server, AWS Instance Metadata Service, or process attributes) and issues a short-lived X.509 certificate (SVID) that the workload uses for all authentication.

# SPIRE registration: bind workload identity to platform attributes
spire-server entry create \
    -spiffeID spiffe://production.example.com/ns/payments/sa/payment-api \
    -parentID spiffe://production.example.com/node/k8s-worker-01 \
    -selector k8s:ns:payments \
    -selector k8s:sa:payment-api \
    -selector k8s:container-name:payment-api \
    -ttl 3600

# The workload receives an SVID automatically via the Workload API
# No secrets are configured, deployed, or rotated by the application
# The SVID contains:
#   - SPIFFE ID: spiffe://production.example.com/ns/payments/sa/payment-api
#   - X.509 certificate: valid for 1 hour
#   - Private key: generated on the node, never transmitted
#   - Trust bundle: CA certificates for the trust domain

SPIFFE-based identity replaces three categories of shared secrets simultaneously. Service-to-service authentication uses mutual TLS with SVIDs instead of API keys or shared tokens. Vault authentication uses the SVID as proof of identity instead of a Vault token or AppRole secret. CDN edge to origin authentication uses SVIDs instead of shared origin pull secrets.

CDN Edge Credential Injection

CDN edge servers present a unique credential management challenge. They run in distributed, potentially hostile environments (third-party data centers, ISP co-location facilities) where storing long-lived secrets creates unacceptable risk. Three patterns address this without static credentials at the edge.

Just-in-time credential injection

The edge function authenticates to a Vault instance using its CDN platform identity and receives a short-lived credential scoped to a single origin request. The credential is held in memory for the duration of the request and discarded immediately after use.

# CDN edge function: just-in-time origin credential
async function handleRequest(request) {
  // Authenticate to Vault using CDN platform identity
  // (CDN provider signs attestation with its workload identity)
  const vaultToken = await authenticateToVault({
    method: "jwt",
    jwt: getCDNPlatformToken(),
    role: "cdn-edge-origin-pull"
  });

  // Get ephemeral origin credential (TTL: 5 minutes)
  const cred = await vaultToken.read("secret/data/origin-pull");

  // Use credential for single origin request
  const originResponse = await fetch(request.url, {
    headers: {
      "Authorization": `Bearer ${cred.data.token}`,
      "X-Edge-POP": getEdgeLocation()
    }
  });

  // Credential goes out of scope — no persistence
  return originResponse;
}

HMAC-based origin verification

Instead of sharing a secret between edge and origin, the CDN generates a per-request HMAC signature using a key stored in the CDN provider's key management service. The origin verifies the signature using the corresponding verification key, which was distributed during initial setup through a secure channel. No shared secret needs to exist at the edge.

This pattern works particularly well with origin protection configurations where the origin needs to verify that requests come from the legitimate CDN edge rather than direct attackers, without introducing a shared credential that could be exfiltrated from edge infrastructure.

Automated Rotation Infrastructure

Even in systems that primarily use ephemeral credentials, some secrets resist full dynamic generation: third-party API keys, TLS certificates for public endpoints, encryption keys for data at rest. These require automated rotation with zero-downtime overlap.

Zero-Downtime Secret Rotation with Dual-Active Keys Zero-Downtime Secret Rotation: Dual-Active Pattern T0 T1: Generate new T2: Promote new T3: Retire old Key A (original) — Active: signing + verification Primary key for all operations until T2 Key A: verification only Retired Key B: standby Key B (new) — Active: signing + verification Primary key for all operations from T2 Overlap window: both keys verify Tokens signed by either key are valid Vault automates: generate (T1) → distribute (T1-T2) → promote (T2) → retire old (T3) → repeat
Figure 2: Zero-downtime rotation uses a dual-active overlap window where both old and new keys validate requests. New signatures use the new key while the old key remains valid for in-flight requests.

The dual-active rotation pattern ensures continuity. At T1, a new key is generated and distributed to all verification endpoints (including CDN edge nodes). At T2, the new key becomes the signing key while the old key continues to verify in-flight tokens. At T3, after all tokens signed with the old key have expired, the old key is retired. This pattern applies to JWT signing keys, API authentication keys, and TLS certificates.

# Vault auto-rotation policy for API keys
path "secret/data/api-keys/stripe" {
  capabilities = ["read"]
}

# Rotation job (runs via Vault's periodic function)
# 1. Generate new key via Stripe API
# 2. Store both keys in Vault (primary + previous)
# 3. Services fetch the primary key on next credential request
# 4. After 72h overlap, delete the previous key

# Application code: always fetch current credential
import hvac

def get_stripe_key():
    client = hvac.Client()
    # Authenticated via SPIFFE SVID
    secret = client.secrets.kv.v2.read_secret("api-keys/stripe")
    return secret["data"]["data"]["primary_key"]
    # No hardcoded key. No .env file. No rotation coordination.

Monitoring Credential Health

Zero-trust credential management requires continuous monitoring to detect regressions: a developer who commits a secret, a service that falls back to static credentials, or a Vault lease that fails to renew. Key metrics include the ratio of dynamic to static credentials in use, the maximum credential age across all active leases, the time between credential rotation events, and the count of credentials with TTLs exceeding the target maximum.

Alert on any credential that lives longer than four hours unless it is explicitly classified as a long-lived key with compensating controls (IP restriction, usage monitoring, anomaly detection). Alert on Vault lease renewal failures because a failed renewal means a service is either using a stale credential or has fallen back to a static one.

The transition from shared secrets to ephemeral credentials is not an all-or-nothing migration. Start with the highest-risk credentials (production database passwords, payment processor keys) and work outward. Each credential you move to dynamic generation reduces the blast radius of a potential compromise and simplifies your incident response playbook. The goal is a state where credential rotation is continuous, automatic, and invisible to application developers, because the infrastructure handles identity and credential lifecycle without human intervention.

External References

References & Standards: RFC 7234 · HTTP/2 specification