Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.
Introduction
Just-in-Time (JIT) access replaces standing privileged accounts with time-bound elevation: a user or workload requests a role, an approval or policy check runs, and access is granted for a short, defined window before it automatically expires. Secrets rotation applies the same time-bound principle to credentials themselves — API keys, database passwords, service-account keys — replacing them on a schedule or after use so that a leaked secret has a short shelf life rather than being valid indefinitely.
Both controls attack the same problem from different angles: the blast radius of a compromised credential is a function of how much it can do and for how long. Standing admin access and long-lived static keys maximize both dimensions; JIT and rotation minimize them. Neither control prevents a compromise, but both shrink the window in which a stolen credential is actually usable, which is frequently the difference between a contained incident and a full domain or cloud account compromise.
Attack Prerequisites
Defeating JIT access or rotation, or exploiting gaps in how they’re implemented, generally requires one of:
- An approval workflow that can be bypassed or self-approved — a break-glass path, a misconfigured policy that auto-approves certain requesters, or an approver account that is itself compromised.
- Grants with excessive TTL or scope — a JIT elevation that lasts hours or days rather than minutes, or grants a broader role than the task requires, giving an attacker who compromises the session a wide window.
- A rotation mechanism with its own static, over-privileged credential — the Lambda/service account that performs rotation is often more powerful than the secret it rotates, and if it is itself long-lived and over-scoped, compromising it defeats the purpose of rotation entirely.
- Secret caching that outlives rotation — an application that reads a secret at startup and never reloads it will keep using the old value even after the secrets manager rotates it, silently reintroducing a long-lived credential in practice.
How It Works
JIT access is typically brokered by a Privileged Access Management (PAM) tool or cloud-native mechanism: HashiCorp Boundary or Vault, CyberArk, or AWS IAM Identity Center’s temporary elevated permission sets. The common pattern is request -> approve -> generate short-lived credentials -> auto-revoke. In AWS this is frequently implemented directly with STS: sts:AssumeRole issues temporary credentials (DurationSeconds, capped by the role’s MaxSessionDuration) rather than handing out a long-lived IAM user access key. Vault’s dynamic secrets engines go further, generating a unique, scoped database or cloud credential per request with a lease that Vault itself revokes at expiry — the credential never exists as a static value an operator could leak from a config file.
Secrets rotation works by having the secrets manager, not the application, own the credential’s lifecycle. AWS Secrets Manager’s rotation model uses two staging labels on a secret version — AWSCURRENT (what applications should be using) and AWSPENDING (the new value being validated) — and a rotation Lambda that runs four steps (createSecret, setSecret, testSecret, finishSecret) to generate a new credential, apply it to the target system, verify it works, and only then atomically promote it to AWSCURRENT. This staged approach avoids an outage window where neither the old nor new credential is valid.
The security property both mechanisms rely on is the same: a credential’s usable lifetime should approximate the time it takes to do the legitimate task, not the lifetime of the employee or service. An attacker who steals a JIT-issued or freshly-rotated credential still has to act inside that window, which materially narrows the attack compared to a static key sitting in a CI variable for years.
Practical Example / Configuration
An IAM policy attached to a JIT role commonly starts life over-broad because it was copied from a standing-access policy rather than scoped to the task. This grants far more than an on-call engineer debugging a production database needs, and sets no session duration cap beyond the account default:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "rds:*",
"Resource": "*"
}]
}
// Role trust policy: MaxSessionDuration left at the 3600s default,
// no condition restricting who/when this role may be assumed.
JSONScoped correctly, the JIT grant matches the actual task, restricts the session to the minimum useful duration, and requires MFA and a specific approver context to assume:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["rds:DescribeDBInstances", "rds-db:connect"],
"Resource": "arn:aws:rds-db:us-east-1:111122223333:dbuser:db-ABCDEFGH/oncall",
"Condition": {"Bool": {"aws:MultiFactorAuthPresent": "true"}}
}]
}
// Role: MaxSessionDuration = 900 (15 min); trust policy requires
// the requesting principal to carry a tag set only by the approval
// workflow, e.g. "jit-ticket": "<ticket-id>".
JSONWalkthrough / Exploitation
Standing up JIT elevation with HashiCorp Vault for a short-lived database credential looks like this in practice:
vault write database/roles/oncall-readonly \
db_name=prod-postgres \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' \
VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl=15m max_ttl=1h
# Engineer requests a lease at time of need:
vault read database/creds/oncall-readonly
# Returns a unique username/password pair with a 15m lease.
# Vault auto-revokes (drops the role) at lease expiry.
BashEnabling rotation on an existing AWS Secrets Manager secret and confirming the staged rollout completes cleanly:
aws secretsmanager rotate-secret \
--secret-id prod/app/db-password \
--rotation-lambda-arn arn:aws:lambda:us-east-1:111122223333:function:rotate-db-secret \
--rotation-rules AutomaticallyAfterDays=30
aws secretsmanager describe-secret --secret-id prod/app/db-password \
--query 'VersionIdsToStages'
# Confirm exactly one version is tagged AWSCURRENT after rotation.
BashNote: Rotation only helps if every consumer actually re-reads the new value. Applications that cache a secret at process start need a reload mechanism (SIGHUP handler, SDK with built-in refresh, or a sidecar that restarts the process) — otherwise rotation silently creates a split-brain where the secrets manager has moved on but the running application has not.
Opsec: Break-glass/emergency-access paths deliberately bypass the normal JIT approval flow and are exactly the path a sophisticated attacker will look for. Treat break-glass credentials as Tier-0: single-use, vaulted, alerting on any access, and reviewed after every use.
Detection and Defense
- Log every JIT grant with its requester, approver, scope, and TTL, and alert on any privileged action that did not correspond to an active grant.
- Cap session/lease duration to the shortest value that supports the task, not the platform default.
- Scope the rotation function itself tightly and audit it as a privileged component, since it necessarily holds the ability to read/write the secrets it manages.
- Rotate on compromise indicators, not just on schedule — treat any suspected exposure (leaked in a repo, logged in cleartext, shared over chat) as an immediate rotation trigger.
- Alert on break-glass usage and require post-use review for every invocation.
Real-World Impact
Long-lived static credentials leaking from CI/CD pipelines, source repositories, and misconfigured logging remain a recurring root cause in incident reports across cloud environments — the 2021 Codecov Bash Uploader compromise, in which a modified script exfiltrated environment variables (including CI secrets) from customer build environments, is a widely cited example of how much damage a single long-lived static credential can cause once it escapes its intended boundary. JIT access and automated rotation are the direct structural response: minimize how long any given secret is worth stealing.
Conclusion
Standing privilege and static secrets are a liability that accumulates silently until the day a credential leaks; JIT access and rotation convert that liability into a bounded, auditable window. The controls are only as strong as their weakest link — an over-broad grant, an over-privileged rotation function, or an application that never reloads — so implementing them well means scoping and auditing the mechanism itself with the same rigor as the credentials it protects.
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments