privileged.id

Privileged access

Service accounts and secrets

Most identities are not people. How to give workloads unique, least-privilege identities, replace long-lived secrets with short-lived tokens, and manage the secrets that remain.

Reviewed 6 min read 4 primary sources

Most identities in a modern environment are not people. Applications, pipelines, scheduled jobs, containers and devices all authenticate, and they usually do it with a secret: a password, an API key, a client secret or a private key. These non-person entities are easy to create, rarely reviewed and often over-privileged. Their secrets end up in places nobody intended. This page covers what SP 800-53 asks for, why long-lived secrets are the core problem, how short-lived credentials and workload identity federation remove many of them, and how to handle the secrets that remain.

Non-person entities and the controls that cover them

SP 800-53 Rev. 5 treats service identities as accounts like any other. The AC-2 discussion lists "system" and "service" accounts among account types, and the AC-6 discussion applies least privilege to "system processes". Two controls are specific to them or matter most for them:

  • IA-9, Service Identification and Authentication: "Uniquely identify and authenticate [organization-defined system services and applications] before establishing communications with devices, users, or other services or applications." Its discussion mentions digital certificates as one method, and notes that in distributed systems the authentication decision can be passed to the services that act on it, instead of the raw credentials.
  • IA-5, Authenticator Management. Its items apply directly to service credentials: change default authenticators before first use (e), change or refresh authenticators on a defined schedule or after defined events (f), protect authenticator content from disclosure and modification (g), and change authenticators for group or role accounts when membership changes (i).

SP 800-207 flags a specific risk in section 5.7: software agents "may have a lower bar for authentication (e.g., API key versus MFA)" than a human doing the same administrative task. An attacker who obtains such an agent's credential can then act as that agent.

Secrets sprawl

A long-lived secret gets copied every time someone needs it. Typical places it ends up:

  • Source code and configuration files committed to version control, including history and forks.
  • CI/CD variables, build logs and cached artifacts.
  • Container images and virtual machine templates.
  • Environment variables visible to every process and crash dump on the host.
  • Tickets, chat messages and wiki pages written while debugging.
  • Developer laptops and shell history.

IA-5(7) addresses the first of these directly: ensure that "unencrypted static authenticators are not embedded in applications or other forms of static storage". Its discussion closes a common loophole. If a stored authenticator is "used in the manner stored", it counts as unencrypted. Our reading: Base64, or encryption with the key stored next to the ciphertext, should be treated as unencrypted too.

Short-lived credentials and workload identity federation

The most effective fix for a long-lived secret is to stop needing one. Many platforms (cloud compute, container orchestrators, CI systems) can give a running workload a signed, short-lived identity token, often a JWT, that says which workload it is. The workload presents that token to an authorization server and receives a short-lived access token in exchange. No static secret is stored anywhere.

Workload identity federation: the platform issues a signed token to the workload, the workload exchanges it at the authorization server for a short-lived access token, then calls the API. Platform token issuer Workload no stored secret Authorization server API 1. signed identity token (minutes) 2 3 fetch issuer keys to verify signature 4. call API with access token
Step 2 presents the platform token (as an RFC 7523 JWT bearer grant, a client assertion, or an RFC 8693 token exchange). Step 3 returns a short-lived access token scoped by the authorization server's trust policy.

The standard pieces

Two IETF specifications provide the exchange step. Products wrap them in their own configuration.

  • RFC 7523 (Standards Track) defines using a JWT as an OAuth 2.0 authorization grant (grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer) and as client authentication (client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer).
  • RFC 8693 defines OAuth 2.0 Token Exchange (grant_type=urn:ietf:params:oauth:grant-type:token-exchange), where a subject_token of a stated subject_token_type is exchanged for a new token.

An access token request using the RFC 7523 grant looks like this (line breaks for display only, token shortened):

POST /token HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6IjNmIn0.eyJpc3MiOi...
&scope=deploy%3Astaging

The decoded payload of the platform-issued assertion might be:

{
  "iss": "https://ci.example.com",
  "sub": "repo:example/payments:ref:refs/heads/main",
  "aud": "https://as.example.com/token",
  "iat": 1792998000,
  "exp": 1792998300,
  "jti": "a1f3c9e2-7b44-4b0e-9f1d-2c8e5d0f6a71"
}

RFC 7523 section 3 lists what the authorization server MUST check. The JWT MUST contain iss, sub, aud and exp. The server MUST reject a JWT that does not name it as an audience, MUST reject an expired JWT (allowing for clock skew), and MUST reject an invalid signature or MAC. jti is optional, and the server MAY use it to prevent replay.

For calls between services where the receiving side should only accept the token from its rightful holder, see sender-constrained tokens (mTLS and DPoP).

When you still need a secret

Some systems only accept a password or a static key. For those, a secrets manager (a central service that stores secrets, controls who can read them and logs every read) reduces sprawl. Generic capabilities to look for and use:

  • Per-workload access policy: each workload can read only its own secrets, and authenticates to the secrets manager with its platform identity rather than another static secret.
  • Audit of every read: who or what retrieved which secret, when.
  • Dynamic or leased secrets: the manager creates a database user or cloud credential on request with a time to live, then removes it.
  • Rotation: scheduled and on demand. IA-5 f leaves the period to you; set it per authenticator type.

Rotation without outages

Rotation fails in practice when it breaks things. A common pattern is two valid credentials at once: create the new secret, deploy it to every consumer, confirm that nothing still uses the old one, then revoke the old one. Systems that allow only one active secret per account force a hard cutover. That is a reason to prefer certificates or federation for those systems.

Detection: finding secrets that leaked

  • Scan before commit (local hooks) and on every push and pull request, and block merges with detected secrets.
  • Scan history, build logs, container images and wiki exports, not just the current branch.
  • When a secret is found: revoke or rotate first, then clean up. Removing it from the latest commit does not remove it from history, forks or anyone's clone.
  • Watch the authorization server and API logs for credentials used from unexpected networks or at unexpected volumes (AC-2(12) atypical usage applies to service accounts too).

Ownership

AC-2 b asks you to assign account managers. For service identities, that means a named human owner who can answer three questions: what uses this identity, what does it need, and what breaks if it is removed. Ownerless service accounts are the ones nobody dares to rotate or delete. Record the owner, purpose, permissions, credential type, rotation date and last use. Include service identities in access reviews, and transfer ownership when the owner moves or leaves.

Checklist

  • Every service identity is unique to one workload, not shared across applications (IA-9).
  • Platform-issued identity and short-lived tokens are used wherever the target supports them.
  • Federation trust policies pin the issuer and match the subject narrowly.
  • No secrets in source code, images or static configuration (IA-5(7)). Scanners run on every push.
  • Remaining secrets live in a secrets manager, readable only by their workload, with every read logged.
  • Default credentials changed before first use (IA-5 e). Rotation schedule defined per type (IA-5 f).
  • Permissions scoped to the actions and resources actually used (least privilege).
  • Each service identity has a named owner and appears in access reviews.

Sources

  1. NIST SP 800-53 Rev. 5: Security and Privacy Controls for Information Systems and Organizations, NIST. Public domain (U.S. government work).
  2. NIST SP 800-207: Zero Trust Architecture, NIST. Public domain (U.S. government work).
  3. RFC 7523: JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants, IETF. IETF Trust Legal Provisions (brief quotations with attribution).
  4. RFC 8693: OAuth 2.0 Token Exchange, IETF. IETF Trust Legal Provisions (brief quotations with attribution).