Claude Skill

sota-identity-access

State-of-the-art identity and access management engineering (2026) for BUILDING/configuring identity infrastructure AND AUDITING it — federation, IdPs, authorization models, the joiner-mover-leaver lifecycle, privileged/break-glass access, workload identity, MFA/passkeys, and ass

LLM Mart · 0 points · 7 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download martinholovsky-sota-skills-skills_sota-identity-access-582d6f9.zip · 38 KB
Part of martinholovsky/sota-skills — 39 skills

Install

skills CLI npx skills add https://github.com/martinholovsky/SOTA-skills/tree/main/skills/sota-identity-access
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install martinholovsky-sota-skills@llmmart
Git git clone https://github.com/martinholovsky/SOTA-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole martinholovsky/sota-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

SOTA Identity & Access

Purpose

Own the identity infrastructure and access-management design of a system: the federation protocols themselves, the IdP that issues and validates tokens, the authorization model that decides who may do what, the lifecycle that creates and destroys access, privileged access, and machine identity. Two modes. In BUILD mode you stand up or configure this infrastructure correctly by default. In AUDIT mode you assess an existing identity estate against the same rules and report severity-rated findings. The rules files are the single source of truth for both.

Boundary discipline — this skill does not re-teach what siblings own:

  • App-level authn ceremony (password storage/argon2id, session cookie flags, WebAuthn ceremony, JWT signature validation mechanics at one RP): that is sota-code-security rules/02. This skill owns the protocol and the IdP side.
  • App-level object/function authz (IDOR/BOLA in one service's handlers): that is sota-code-security rules/03. This skill owns the authorization model and the policy engine that the app calls.
  • Secret storage, OIDC-federation mechanics for workloads, JWT kid rotation as a credential operation: sota-secrets-management rules/01 and rules/05.

Concurrent siblings to invoke alongside: sota-network-security (mTLS, ZTNA, identity-aware proxy), sota-kubernetes (K8s RBAC, OIDC to the API server, SA tokens), sota-detection-engineering (identity-based detections, impossible-travel, auth anomaly), sota-privacy-compliance (consent, DSAR, audit evidence).

The hierarchy of preference, always: (1) no standing credential — short-lived, federated, sender-constrained tokens; (2) standing identity with strong phishing-resistant authentication and just-in-time elevation; (3) long-lived secret-authenticated client with rotation and audit; (4) anything static and broadly-scoped is a defect to be justified or removed.

BUILD mode

Use when standing up or configuring any identity component.

  1. Pick the protocol, not the vibe. Interactive user login → OIDC Authorization Code + PKCE (the only sanctioned interactive flow). Service-to-service → client credentials with private_key_jwt/mTLS, or workload identity federation. High assurance → FAPI 2.0. Legacy SAML only where a relying party requires it. Read rules/01-federation-protocols.md before configuring any client.
  2. Treat the IdP as a tier-0 asset. HA, backups of the identity store, restricted admin plane, signing-key rotation, break-glass design. rules/02-idp-operations.md.
  3. Design the authorization model deliberately. RBAC vs ABAC vs ReBAC is an architecture decision; model roles/relationships and write policy as code with a test matrix. rules/03-authorization-models.md.
  4. Wire the lifecycle before launch. Joiner-mover-leaver, SCIM provisioning AND deprovisioning, access reviews. Deprovisioning is the #1 IAM failure — design it first. rules/04-lifecycle-provisioning.md.
  5. Separate and time-box privilege. Admin-account separation, JIT elevation, logged-and-alerted break-glass, machine identity. rules/05-privileged-workload.md.
  6. Make authentication phishing-resistant and adaptive. Passkeys/FIDO2 at the IdP, step-up, CAEP/SSF for continuous evaluation. rules/06-mfa-federation-assurance.md.
  7. Self-review against each file's Audit checklist before declaring done.

AUDIT mode

Use when assessing an existing identity estate.

Sweep procedure

  1. Enumerate the IdP config: clients/relying parties and their redirect URIs, client-auth methods, token lifetimes, grant types enabled, signing keys + rotation, session/SLO config, MFA policy, federation/brokering trusts. Pull from the IdP API or config export, not screenshots.
  2. Enumerate the population: every human and service account, its authentication strength, last-login, group/role assignments, and owner. Cross against the HR/source-of-truth roster to find orphans.
  3. Sweep by rules file: 01 (protocol/token misconfig), 02 (IdP hardening), 03 (over-privilege/SoD), 04 (orphaned/dormant/no-reviews — usually the most findings), 05 (break-glass/standing admin/static workload creds), 06 (weak MFA).
  4. Verify, don't assume: a wildcard redirect URI, an account that logged in 400 days ago, a role granting * — confirm each against the live config/logs before reporting. Never authenticate as a discovered account or trigger break-glass without explicit permission.

Severity conventions

Severity Definition Examples
Critical Identity-layer flaw enabling full account/tenant takeover or auth bypass for many principals Wildcard/loose redirect_uri enabling token theft; IdP accepts unsigned SAML assertions or alg:none; standing super-admin with no MFA; signing key never rotated and leaked; OIDC issued to an open-redirect client
High Compromise of a single privileged identity, or systemic over-grant Orphaned admin account still active post-termination; break-glass account with a static shared password and no alerting; role granting estate-wide *; long-lived non-rotating refresh tokens; SSO with no Single Logout on credential change
Medium Weak lifecycle/assurance on a contained scope No access reviews/recertification; dormant non-priv accounts; phishable MFA (SMS/TOTP) where phishing-resistant is feasible; client_secret_basic where private_key_jwt/mTLS is supported; missing SoD on sensitive role pairs
Low Hygiene and defense-in-depth gaps No idle session timeout; consent screen not informative; no dormant-account detection job; PAR/DPoP available but unused for a low-risk client; missing azp validation on a single-audience token
Info Observations and accepted risk Legacy SAML RP documented and owner-acknowledged; planned migration off SMS MFA tracked

Finding format

Report every finding as one line, ordered Critical → Info:

file:line | rule | severity | effort (trivial/small/medium/large) | fix

Where file:line anchors to the offending config (e.g. keycloak/realm.json:412, policies/rbac.rego:88, or idp://clients/web-app#redirect_uris for live config with no file). rule is the rules-file section (e.g. 01 §redirect-uri). Group repeated instances of one weakness into a single finding listing all locations. End the audit with: counts per severity, the orphaned/dormant account tally, and the top 3 systemic fixes (almost always: deprovisioning automation, MFA hardening, least-privilege roles).

Rules index

File Read this when...
rules/01-federation-protocols.md Configuring or auditing OIDC/OAuth flows, choosing a grant type, validating tokens at the RP, PKCE/PAR/RAR/JAR/DPoP, OAuth 2.1 & FAPI 2.0, SAML and its attack classes (XSW, comment injection, unsigned assertions), SCIM as a protocol, redirect-URI matching, token-validation pitfalls
rules/02-idp-operations.md Running a self-hosted IdP (Kanidm/Keycloak/Authentik/Zitadel), client/RP registration discipline, client-auth ladder, token lifetimes + refresh rotation + reuse detection, signing-key (kid) rotation, session management + Single Logout, consent, multi-IdP brokering, IdP as tier-0 (HA/backup)
rules/03-authorization-models.md Choosing/designing RBAC vs ABAC vs ReBAC, role modeling and role explosion, the group→role mapping discipline, least privilege + segregation of duties, policy-as-code engines (OPA/Rego, Cedar, OpenFGA, SpiceDB), policy testing, birthright vs requested access
rules/04-lifecycle-provisioning.md Designing or auditing joiner-mover-leaver, SCIM-driven provisioning/deprovisioning, the orphaned-account problem, access reviews/recertification, just-in-time provisioning, dormant-account detection
rules/05-privileged-workload.md Admin-account separation, break-glass design (logged/time-bound/alerted, the Kanidm recover-account pattern), JIT/just-enough elevation, session recording, vaulting; machine/workload identity (SPIFFE/SPIRE, workload identity federation, mTLS identity, short-lived over static)
rules/06-mfa-federation-assurance.md Phishing-resistant MFA (FIDO2/passkeys/WebAuthn at the IdP), step-up/adaptive/conditional access, CAEP/SSF continuous evaluation, push-bombing/MFA-fatigue defenses, B2B/B2C/social-login and account-linking risks, identity proofing and NIST 800-63-4 IAL/AAL/FAL
rules/07-active-directory.md Hardening on-prem Active Directory / Kerberos / ADCS: Enterprise Access Model & tiering (ESAE/red-forest retired), delegation risks (unconstrained/constrained/RBCD), Kerberoasting/AS-REP + gMSA/dMSA & AES-only service accounts, SPN hygiene, machine-account quota, NTLM relay (SMB/LDAP signing + channel binding), ADCS ESC classes + template/enrollment-agent hardening + strong cert mapping (KB5014754), LAPS/Protected Users/Credential Guard/auth silos, krbtgt rotation, hybrid Entra sync boundary (PHS/PTA). Detection lives in sota-detection-engineering rules/07

Top-10 non-negotiables

Violations are findings regardless of context; in BUILD mode they are never shortcuts.

  1. Authorization Code + PKCE is the only sanctioned interactive flow. Implicit and ROPC/password grant are dead and disabled at the IdP. (rules/01)
  2. Exact redirect-URI matching, no wildcards, no scheme/host/path looseness. A loose redirect_uri is a token-exfiltration primitive. (rules/01)
  3. At the RP, pin algorithms and validate iss, aud, exp, and nonce; reject unsigned tokens and alg:none. SAML RPs reject unsigned assertions and validate the signature over the whole response with anti-XSW canonicalization. (rules/01)
  4. The IdP is a tier-0 asset: HA, backed-up identity store, restricted admin plane, rotating signing keys with kid overlap, no standing super-admin without phishing-resistant MFA. (rules/02)
  5. Refresh tokens rotate with reuse detection, or are sender-constrained (DPoP/mTLS); access tokens are short-lived. No non-expiring tokens. (rules/01, rules/02)
  6. Authorization is least-privilege by an explicit model with policy-as-code and a tested allow/deny matrix. No role grants estate-wide *; segregation of duties enforced on sensitive pairs. (rules/03)
  7. Group→role mapping is explicit and default-deny: a user with no matching mapping gets no access, never a silent default role. (rules/03)
  8. Deprovisioning is automated and prompt — a leaver loses all access within the agreed SLA, source-of-truth driven via SCIM; access is recertified on a schedule. Deprovisioning is the #1 IAM failure. (rules/04)
  9. Privileged access is separated, just-in-time, and time-boxed; break-glass is logged, alerted, and expires. No permanent quiet admin backdoor. (rules/05)
  10. Phishing-resistant MFA (FIDO2/passkeys) at the IdP for all privileged and, ideally, all users; step-up for sensitive operations; SMS/voice OTP is not phishing-resistant. (rules/06)
Files (sota-skills)
  • rules
    • 01-federation-protocols.md 12.9 KB
      # 01 — Federation Protocols & Their Attack Catalog
      
      Scope: the wire protocols of federated identity and how they fail — OIDC/OAuth 2.x
      flows and token validation at the relying party (RP), OAuth 2.1 and FAPI 2.0, the
      sender-constraining and request-integrity extensions (PKCE, PAR, RAR, JAR, DPoP, mTLS),
      SAML 2.0 and its attack classes, and SCIM 2.0 as a provisioning protocol.
      
      This file owns **protocol design and the IdP/RP token contract**. It does NOT own the
      app-side JWT *signature-verification code path* or session cookie handling — that is
      **sota-code-security** rules/02. When the finding is "this Express middleware does not
      pin the alg," route it there; when it is "the IdP allows the implicit flow" or "the RP
      never checks `aud`," it is here.
      
      ## 1. OIDC / OAuth: only Authorization Code + PKCE for interactive flows
      
      - **Authorization Code + PKCE (RFC 7636) is the only sanctioned interactive flow** — for
        confidential *and* public clients. PKCE binds the authorization request to the token
        request via a `code_verifier`/`code_challenge` (use `S256`, never `plain`).
      - **Implicit flow is dead.** It returns tokens in the URL fragment (leak via history,
        referrer, logs) with no client authentication. OAuth 2.1 (`draft-ietf-oauth-v2-1`,
        draft-15, March 2026 — still an Internet-Draft, *not* an RFC) removes it. Disable
        `response_type=token`/`id_token token` at the IdP.
      - **ROPC / password grant is dead.** It hands the user's password to the client,
        defeats federation and MFA, and is removed in OAuth 2.1. Disable
        `grant_type=password`.
      - **Client credentials** for machine-to-machine only (no end user present).
      - **Device Authorization Grant (RFC 8628)** for input-constrained devices.
      - The current security baseline is **OAuth 2.0 Security Best Current Practice, RFC 9700
        (January 2025)**: PKCE for all auth-code flows, exact redirect-URI matching, refresh
        rotation or sender-constraining, short-lived access tokens.
      
      ```
      # GOOD: IdP client config — interactive web app
      grant_types         = ["authorization_code", "refresh_token"]
      response_types      = ["code"]
      require_pkce        = true        # S256
      token_endpoint_auth = "private_key_jwt"   # not client_secret_basic
      # BAD
      grant_types    = ["authorization_code", "implicit", "password"]   # implicit + ROPC live
      require_pkce   = false
      ```
      
      ## 2. Token types: ID token vs access token vs userinfo
      
      - **ID token** authenticates the *user to the client*. It is a JWT for the RP to
        consume. Never send it to a resource server as a credential.
      - **Access token** authorizes the *client to a resource server*. Opaque or JWT; the RP
        treats it as bearer (or sender-constrained). The client must not parse/depend on its
        contents unless it is the audience.
      - **UserInfo endpoint** returns fresh claims for the access token's subject. Use it when
        claims may have changed since token issuance; do not stuff every attribute into the ID
        token.
      - A frequent confusion bug: the client validates the *access* token as if it were the ID
        token, or forwards the ID token as the API bearer. Keep the roles distinct.
      
      ## 3. Required claim validation at the RP (the highest-yield audit area)
      
      Validate **every** ID token (OpenID Connect Core 1.0):
      
      - `iss` — exact string match to the configured issuer. Mismatched/missing `iss` =
        accept-any-IdP.
      - `aud` — must contain *this* client's `client_id`. Missing `aud` check = a token minted
        for client B is accepted by client A. If `aud` is an array or `azp` is present, verify
        `azp` equals your `client_id`.
      - `exp` — reject expired; enforce small clock skew (≤60s). Also `nbf`/`iat` sanity.
      - `nonce` — the RP sends a `nonce` in the auth request and verifies it echoes in the ID
        token (binds token to *this* login, anti-replay). REQUIRED for implicit/hybrid; send
        and check it for auth-code too.
      - Signature — pin allowed `alg` to the IdP's actual signing alg(s) (e.g. `RS256`,
        `ES256`); fetch keys from the IdP `jwks_uri`; **reject `alg:none` and reject
        symmetric `alg` when an asymmetric key is expected** (the RS256→HS256 confusion
        attack: a verifier that trusts the header `alg` can be tricked into HMAC-verifying with
        the public key as the secret).
      
      ```
      # BAD — accepts any issuer, no audience, trusts header alg
      claims = jwt.decode(token, key, verify_aud=False)   # aud unchecked
      # GOOD
      claims = verify(token,
          issuer="https://idp.example.com",
          audience="web-app",
          algorithms=["ES256"],          # pinned; no 'none', no HS*
          require=["iss","aud","exp","iat","nonce"])
      assert claims.get("azp", claims["aud"]) == "web-app"
      ```
      
      - **Mix-up defense when the RP/broker talks to more than one AS** (Kanidm *plus* any
        upstream/social IdP): validate the `iss` **authorization-response** parameter
        (RFC 9207), not just the ID-token `iss`. Without it, an attacker who can make the
        user start a login at an honest AS can swap in a malicious AS's authorization
        response and have the code/token redeemed at the wrong endpoint. Single-AS
        deployments are unaffected, but wire it in before adding a second IdP.
      
      - **Discovery & JWKS**: configure from `/.well-known/openid-configuration` (OpenID
        Connect Discovery 1.0), cache the `jwks_uri` keys, and honor key rotation by `kid`
        (re-fetch on unknown `kid`; do not pin a single key forever). Cache JWKS with a sane
        TTL; a hammering RP that re-fetches per request is a DoS on the IdP.
      
      ## 4. Redirect-URI discipline (Critical when loose)
      
      - Register **exact, absolute** redirect URIs. **No wildcards** (`https://app/*`), no
        scheme downgrade (`http`), no trailing-slash/path looseness, no
        open-host patterns. The IdP must match the requested `redirect_uri` against the
        registered set by **exact string compare**.
      - Loose matching is a token-theft primitive: an attacker who can satisfy a wildcard
        (`https://app.example.com.attacker.com/cb`, `https://app/.../@evil`, an open redirect
        on the registered host) receives the code/token.
      - Per-client registration: each RP gets its own client with its own narrow redirect set.
        Never share one client across apps.
      
      ```
      # BAD
      redirect_uris = ["https://app.example.com/*", "http://localhost"]
      # GOOD
      redirect_uris = ["https://app.example.com/auth/callback"]   # exact, https, fixed path
      ```
      
      ## 5. Request integrity & sender-constraining extensions
      
      Adopt these for high-value and high-assurance clients; required by FAPI 2.0.
      
      - **PAR — Pushed Authorization Requests, RFC 9126**: the client POSTs the authorization
        request to the IdP back-channel and receives a `request_uri`; the front-channel URL
        carries only that reference. Removes request-tampering and parameter-injection on the
        redirect.
      - **RAR — Rich Authorization Requests, RFC 9396**: `authorization_details` carries
        fine-grained, structured authorization (e.g. "transfer ≤€100 from account X") instead
        of coarse scopes. Use for transactional authorization.
      - **JAR — JWT-Secured Authorization Request, RFC 9101**: the request parameters are a
        signed (optionally encrypted) JWT, giving request integrity/authenticity.
      - **DPoP — Demonstrating Proof of Possession, RFC 9449**: sender-constrains access and
        refresh tokens by binding them to a client-held key proven per request via a `DPoP`
        header. A stolen DPoP-bound token is useless without the private key. The
        application-layer alternative to mTLS-bound tokens.
      - **mTLS client auth & certificate-bound tokens — RFC 8705**: client authenticates with
        a TLS client cert; tokens are bound to the cert thumbprint. Strongest client auth /
        token binding where a PKI exists — coordinate with **sota-network-security** (mTLS).
      - **PKCE downgrade**: if the IdP *supports* but does not *require* PKCE, a MITM can strip
        the `code_challenge`. Mitigation: the IdP rejects a token request with a
        `code_verifier` when no challenge was registered, and rejects an auth-code request
        without a challenge for clients configured to require PKCE. Enforce, don't merely
        offer.
      
      ## 6. OAuth 2.1 and FAPI 2.0 posture
      
      - **OAuth 2.1**: a consolidation draft (obsoletes 6749/6750/8252, folds in RFC 9700). It
        is not yet an RFC — treat its *mandates* (PKCE everywhere, no implicit, no ROPC, exact
        redirect URIs) as today's baseline regardless, because they are independently in force.
      - **FAPI 2.0 Security Profile** is **Final (22 February 2025)**; FAPI 2.0 Message Signing
        finalized later in 2025. For high-assurance (open banking, health, government) profiles
        require: PAR (RFC 9126) and reject non-PAR requests; **sender-constrained tokens via
        DPoP (9449) or mTLS (8705)**; PKCE S256; exact redirect URIs; tight token lifetimes.
        Reach for FAPI 2.0 when the blast radius of a stolen token is financial or regulated.
      
      ## 7. SAML 2.0 and its attack classes
      
      SAML 2.0 (OASIS, 2005) remains common for enterprise SSO; new development should prefer
      OIDC. When you run or consume SAML, the failure modes are signature-handling bugs:
      
      - **XML Signature Wrapping (XSW)**: the attacker wraps a forged assertion so the
        signature-validation logic and the business logic resolve *different* elements
        ("validate this signed node, but read that injected node"). Defense: validate the
        signature over the element you actually consume; resolve assertions by the same
        reference the signature covers; use a hardened SAML library, schema-validate, and
        reject documents with multiple/extra assertions.
      - **Comment-injection / canonicalization truncation** (Duo, 2018): canonicalization
        drops a comment node before signature check, but naive text extraction reads only the
        first text node — `admin@corp.com<!---->.evil.com` authenticates as `admin@corp.com`.
        Defense: extract the *full* node text (concatenate text nodes) or use a library patched
        for this; don't `getFirstChild().getNodeValue()`.
      - **Unsigned-assertion / signature-exclusion**: the RP accepts a response/assertion with
        no signature, or validates only the *first* assertion while consuming a second.
        Defense: require a valid signature on the response **or** the assertion you consume,
        fail closed when absent, and reject extra assertions.
      - **IdP-initiated SSO risks**: no `InResponseTo` binding → login CSRF and assertion
        replay. Prefer SP-initiated flows; if IdP-initiated is required, enforce single-use
        assertion IDs, tight `NotOnOrAfter`, audience restriction, and RelayState validation.
      - Always enforce: `Destination`/`Recipient` checks, `AudienceRestriction`, assertion
        replay cache, signed metadata, and a rotation plan for IdP signing certs.
      
      ## 8. SCIM 2.0 as a protocol
      
      - **SCIM 2.0** = RFC 7642 (requirements), RFC 7643 (core schema: User, Group), RFC 7644
        (protocol — REST CRUD + PATCH + bulk + filtering). It is the standard for
        cross-domain user provisioning/deprovisioning; lifecycle *usage* is rules/04.
      - Protocol-level hardening: authenticate the SCIM endpoint (bearer/OAuth, not a static
        shared secret in a header), authorize per-tenant, validate filters to avoid injection,
        rate-limit, and treat `active=false` / DELETE as the deprovisioning trigger (don't
        leave a "soft-deleted but still-authenticating" account).
      - **SCIM Security Events — RFC 9967 (May 2026)**: the SCIM Profile for Security Event
        Tokens (SETs; updates RFC 7643/7644) is now the standard mechanism for asynchronous,
        event-driven provisioning signals — prefer it over ad-hoc webhooks or polling for
        propagating lifecycle changes across domains.
      
      ## 9. Legacy: WS-Federation
      
      WS-Federation is a legacy WS-* protocol; vendors (Microsoft Entra/ADFS) treat OIDC and
      SAML 2.0 as the strategic protocols and keep WS-Fed only for backward compatibility.
      New integrations: do not adopt WS-Fed; migrate existing ones to OIDC.
      
      ## Audit checklist
      
      - [ ] Is the implicit flow (`response_type=token`/`id_token token`) disabled at the IdP for every client?
      - [ ] Is ROPC / `grant_type=password` disabled?
      - [ ] Is PKCE (S256) required — not merely supported — for all authorization-code clients? `grep -ri "require_pkce\|code_challenge_method"`
      - [ ] Are all `redirect_uri`s exact, absolute, HTTPS, with no wildcards? Hunt config for `redirect_uri.*\*` or `://\*`.
      - [ ] Does every RP validate `iss`, `aud` (and `azp` when present), `exp`, and `nonce` on the ID token? Grep RP code for `verify_aud`, `audience`, `nonce`.
      - [ ] Are token-verification algorithms pinned, with `alg:none` and asymmetric→symmetric confusion rejected?
      - [ ] Does the RP fetch keys from `jwks_uri` and rotate by `kid` (re-fetch on unknown kid), with a sane JWKS cache TTL?
      - [ ] Are high-value/regulated clients on PAR + DPoP/mTLS (FAPI 2.0) rather than bare bearer tokens?
      - [ ] For SAML RPs: is a signature required and validated over the consumed assertion, with XSW and comment-injection defenses, audience restriction, replay cache, and extra-assertion rejection?
      - [ ] Is IdP-initiated SAML avoided or hardened (single-use IDs, tight NotOnOrAfter, RelayState validation)?
      - [ ] Is the SCIM endpoint authenticated/authorized per-tenant, with DELETE/`active=false` actually terminating authentication?
      - [ ] Is any WS-Federation usage documented as legacy with a migration plan to OIDC?
      
    • 02-idp-operations.md 10.7 KB
      # 02 — IdP Operations
      
      Scope: running and configuring an Identity Provider as production infrastructure —
      client/relying-party registration discipline, the client-authentication ladder, token
      lifetimes and refresh-token rotation with reuse detection, signing-key (`kid`) rotation,
      session management and Single Logout, consent, multi-IdP brokering, and treating the IdP
      as a **tier-0 asset** (HA, backup, restricted admin plane).
      
      Applies to self-hosted IdPs — **Kanidm** (Rust, OIDC/OAuth2, WebAuthn), **Keycloak**
      (CNCF Incubating, OIDC/SAML), **Authentik** (goauthentik.io, OIDC/SAML/SCIM),
      **Zitadel** (Go, OIDC/SAML, multi-tenant) — and the same principles map to **Entra ID**
      and **Okta**. Protocol-level token rules are rules/01; this file is operations.
      
      ## 1. The IdP is tier-0
      
      Everything that authenticates to anything depends on the IdP. Treat it like the root CA
      of your access:
      
      - **Availability**: run HA (≥2 nodes / managed multi-AZ). An IdP outage is a total
        authentication outage. Have a documented degraded-mode (cached sessions, longer token
        lifetimes during incident) and a tested failover.
      - **Backup of the identity store**: the user/group/credential database and the signing
        keys are crown jewels. Back them up encrypted, test restore, and store key material
        per **sota-secrets-management**. For Kanidm, back up the database and the
        server's key material; for Keycloak/Zitadel/Authentik, back up the backing Postgres
        *and* the realm/instance config and signing keys.
      - **Admin-plane isolation**: the IdP admin console is not a normal app. Restrict it by
        network (admin VPN / identity-aware proxy — **sota-network-security**), require
        phishing-resistant MFA, separate admin accounts (rules/05), and audit-log every admin
        mutation immutably.
      - **Patch cadence**: an IdP CVE is critical-by-default. Track the vendor's advisories;
        the federation libraries (SAML, JWT) are exactly where signature-bypass bugs land.
      
      ## 2. Client / relying-party registration discipline
      
      Each application is a distinct client with the narrowest config that works:
      
      - **One client per app**, never shared. Exact redirect URIs only (rules/01 §4).
      - **Scopes/claims minimal**: grant only the scopes the app needs; do not enable the
        `groups`/`profile`/`email` claims for a client that does not consume them.
      - **Public vs confidential**: SPAs and native apps are public clients (no secret) and
        MUST use PKCE; server-side apps are confidential and authenticate per the ladder below.
      - **Disable unused grant/response types** per client (no implicit, no ROPC).
      - **Dynamic Client Registration** (RFC 7591), if enabled, must be authenticated and
        policy-gated — open DCR lets anyone mint a client.
      
      ```
      # Kanidm — register an OIDC RP with an exact redirect; group→scope mapping in rules/03
      kanidm system oauth2 create web-app "Web App" https://app.example.com
      kanidm system oauth2 add-redirect-url web-app https://app.example.com/auth/callback
      kanidm system oauth2 update-scope-map web-app app_users openid email groups
      ```
      
      ## 3. Client-authentication ladder (weakest → strongest)
      
      Pick the strongest the platform supports:
      
      1. `client_secret_basic` / `client_secret_post` — a shared secret in the request. Lowest
         tier; the secret is a long-lived bearer credential that leaks. Acceptable only for
         low-risk confidential clients with the secret in a secret manager and rotated.
      2. `client_secret_jwt` — HMAC-signed assertion; still a shared symmetric secret.
      3. **`private_key_jwt`** — the client signs an assertion with its *private* key; the IdP
         verifies with the public key. No shared secret to leak. Preferred for confidential
         clients.
      4. **`tls_client_auth` / mTLS (RFC 8705)** — client authenticates with a TLS client cert,
         enabling certificate-bound tokens. Strongest where a PKI exists.
      
      Treat `client_secret_basic` as a finding when the client could use `private_key_jwt` or
      mTLS. Never embed a client secret in a public client (SPA/mobile) — there is no secret a
      public client can keep.
      
      ## 4. Token lifetimes, refresh rotation, reuse detection
      
      - **Access tokens short-lived** (minutes, single-digit to ~15). The shorter the lifetime,
        the smaller the stolen-token window and the less you depend on revocation.
      - **Refresh tokens rotate**: each use issues a new refresh token and invalidates the
        prior one. Combined with **reuse detection** — if a previously-used (rotated-out)
        refresh token is presented, treat it as theft, revoke the whole token family, and force
        re-auth. This is the RFC 9700 baseline for public clients (rotate **or**
        sender-constrain).
      - **Sender-constrain** refresh/access tokens with DPoP (RFC 9449) or mTLS (RFC 8705) for
        high value (rules/01 §5) so a stolen token is unusable.
      - **No non-expiring tokens.** "Offline" refresh tokens still get an absolute max lifetime
        and idle expiry; long-lived non-rotating refresh tokens are a High finding.
      - **Revocation** (RFC 7009) endpoint available and used on logout/credential-change;
        pair with introspection (RFC 7662) for opaque tokens.
      
      ```
      # GOOD (IdP token policy)
      access_token_lifetime   = 10m
      refresh_token_rotation  = true
      refresh_reuse_detection = true       # revoke family on reused token
      refresh_absolute_max    = 30d
      # BAD
      access_token_lifetime   = 24h
      refresh_token_rotation  = false
      refresh_token_lifetime  = "never"
      ```
      
      ## 5. Signing-key rotation (`kid`)
      
      - The IdP's token-signing keys rotate on a schedule (e.g. quarterly) and immediately on
        suspected compromise. Each key has a `kid`; publish current + previous in the JWKS so
        in-flight tokens verify during the overlap, then retire the old `kid`.
      - Prefer asymmetric signing (RS256/ES256/EdDSA) so RPs verify with public keys and the
        private key never leaves the IdP. Avoid symmetric (`HS256`) signing across trust
        boundaries.
      - This is the IdP-operations side; the credential-rotation mechanics
        (overlap windows, JWKS publication) are also in **sota-secrets-management** rules/05.
      - Audit: a signing key that has never rotated, or a JWKS that publishes only one key with
        no rotation history, is a finding (no clean path to recover from key compromise).
      
      ## 6. Session management & Single Logout
      
      - **Idle + absolute session timeouts** at the IdP SSO session level: idle (re-auth after
        inactivity) and absolute (hard cap regardless of activity). Privileged sessions get
        shorter caps.
      - **Session fixation**: the IdP must issue a fresh session identifier on successful
        authentication and not accept a pre-login session id. (The app-side cookie handling for
        this is **sota-code-security** rules/02.)
      - **Single Logout (SLO) / back-channel logout**: SSO means one credential opens many RPs;
        logout or credential-change must propagate. Configure **back-channel logout** (OIDC
        Back-Channel Logout: the IdP POSTs a logout token to each RP) so a sign-out or
        forced revocation actually ends sessions everywhere. Front-channel-only logout is
        unreliable (depends on browser). SAML SLO has the same goal and the same fragility.
      - On credential change / account disable, *kill live sessions* — pair with CAEP/SSF
        (rules/06) for near-real-time propagation rather than waiting for token expiry.
      
      ## 7. Consent
      
      - For first-party apps, consent may be implicit/skipped. For **third-party** clients,
        show an informative consent screen: which client, which scopes, what data, revocable.
      - Consent is auditable and revocable by the user and by an admin; revoking consent
        revokes the associated tokens. For privacy/regulatory consent (purpose, retention) see
        **sota-privacy-compliance**.
      - Beware "consent phishing": a malicious OAuth app requesting broad scopes. Gate which
        clients may request sensitive scopes; admin-approve high-scope third-party apps.
      
      ## 8. Multi-IdP & brokering
      
      - An **identity broker** (Keycloak/Authentik/Zitadel brokering an upstream IdP, or
        Kanidm fronting OIDC) federates multiple sources. Each upstream trust is a security
        boundary: validate upstream tokens fully (rules/01 §3), pin the upstream issuer, and
        **map external identities to internal accounts deterministically** — link on a verified
        immutable identifier (verified email + `sub`), never on a mutable display field, to
        avoid account-takeover via attribute collision (account-linking attacks, rules/06).
      - Do not blindly trust upstream group/role claims — re-map them through your own
        authorization model (rules/03); an upstream that can assert arbitrary groups must not be
        able to grant your privileged roles.
      - **Disabled must mean disabled — test it.** After disabling an upstream IdP, broker
        link, or client, verify its authentication path actually fails closed. Keycloak
        CVE-2026-3047 (CVSS 8.8) and CVE-2026-2603 (both fixed in 26.5.5, March 2026) let a
        *disabled* SAML client or upstream SAML IdP still complete IdP-initiated broker
        logins — a retired or compromised upstream an admin thought was off kept
        authenticating users into the realm. Patch, and restrict or disable IdP-initiated
        broker endpoints you do not use.
      
      ## Audit checklist
      
      - [ ] Is the IdP run HA with a tested failover and a documented degraded-mode?
      - [ ] Is the identity store (and signing-key material) backed up encrypted, with restore tested?
      - [ ] Is the admin console network-restricted, MFA-gated with separate admin accounts, and immutably audit-logged?
      - [ ] Is there one client per app with exact redirect URIs and minimal scopes (no shared clients, no unused claims)?
      - [ ] Is Dynamic Client Registration disabled or authenticated+policy-gated?
      - [ ] Does each confidential client use `private_key_jwt` or mTLS rather than `client_secret_basic` where supported? Grep config for `client_secret_basic`/`token_endpoint_auth_method`.
      - [ ] Are no client secrets embedded in public (SPA/mobile) clients?
      - [ ] Are access tokens short-lived (≤~15m)?
      - [ ] Is refresh-token rotation enabled with reuse detection (family revocation), or are tokens sender-constrained (DPoP/mTLS)?
      - [ ] Are there any non-expiring / never-rotating refresh tokens? (High finding)
      - [ ] Do signing keys rotate on a schedule with `kid` overlap in the JWKS, using asymmetric algorithms?
      - [ ] Are idle and absolute SSO session timeouts set, with shorter caps for privileged sessions?
      - [ ] Is back-channel (or reliable) Single Logout configured so sign-out / disable ends sessions across all RPs?
      - [ ] Do third-party clients show informative, revocable consent, with high-scope apps admin-gated?
      - [ ] For brokered/upstream IdPs: is the issuer pinned, tokens fully validated, identities linked on a verified immutable id, and upstream group claims re-mapped (not trusted) into the local model?
      - [ ] When an upstream IdP, broker link, or client is disabled, is it tested that its login path fails closed (Keycloak CVE-2026-3047 / CVE-2026-2603 class), with unused IdP-initiated broker endpoints restricted?
      
    • 03-authorization-models.md 9.6 KB
      # 03 — Authorization Models & Design
      
      Scope: the *model* that decides who may do what — RBAC vs ABAC vs ReBAC, role modeling
      and role explosion, the **group→role mapping discipline**, least privilege and
      segregation of duties (SoD), policy-as-code engines (OPA/Rego, AWS Cedar, OpenFGA,
      SpiceDB), policy testing, and birthright vs requested access.
      
      This file owns the **model and the engine**. It does NOT own app-level object-level
      enforcement bugs (IDOR/BOLA in one handler) — that is **sota-code-security** rules/03.
      The division: code-security asks "did this handler check ownership of this row?"; this
      file asks "is the permission model correct, least-privilege, tested, and free of role
      explosion and SoD violations?".
      
      ## 1. Choosing the model
      
      | Model | Decides on | Use when | Engines |
      |---|---|---|---|
      | **RBAC** | Roles → permission sets | Small/medium apps; access maps cleanly to job functions; manageable number of roles | Native IdP roles, Casbin |
      | **ABAC** | Attributes of subject/resource/action/environment (dept, classification, time, device, risk) | Context matters; dynamic conditions; cross-cutting rules | OPA/Rego, Cedar |
      | **ReBAC** | Relationships in a graph ("editor of doc", "member of org that owns folder") | Sharing, nesting, inheritance, per-object grants (Drive/GitHub-like) | OpenFGA, SpiceDB, Ory Keto |
      
      These compose: Cedar and Zanzibar-style engines support RBAC + ABAC + relationships. Pick
      the *simplest* model that expresses your access rules; reach for ReBAC only when
      per-object relationships and inheritance are real requirements, because the relationship
      graph adds operational complexity (traversal depth, consistency, negative permissions).
      
      ## 2. RBAC: role modeling and role explosion
      
      - **Check permissions, not role names.** Authorize on `can(user, "invoice:refund")`, not
        `role == "admin"`, so roles can evolve without code changes. Map roles → permissions in
        one place.
      - **Role explosion** is the classic RBAC failure: combinatorial roles
        (`finance-eu-readonly-q3`) multiply until no one understands them. Defenses: keep roles
        aligned to *job functions*, factor cross-cutting context into ABAC attributes instead
        of new roles, and prune unused roles in access reviews (rules/04).
      - **No permission accumulation across role changes.** When a user moves roles, *recompute*
        their effective permissions — do not append the new role and leave the old (the "access
        creep" of movers, rules/04).
      - **Birthright vs requested.** Birthright access (everyone gets baseline accounts/groups
        on joining) is automatic and minimal; everything beyond is *requested* and *approved*,
        with an owner and an expiry. A privileged role granted as birthright is a finding.
      
      ## 3. The group→role mapping discipline (default-deny)
      
      The most common silent over/under-grant lives in the IdP-group → app-role mapping:
      
      - **Map explicitly and default-deny.** A user whose groups match *no* mapping must get
        *no* role — never a silent "default" role. The dangerous real-world bug: an IdP is
        wired so **every authenticated OIDC user is assigned a low/default app role regardless
        of their group membership**, because the app falls back to a default when no group claim
        maps. That simultaneously *over*-grants (outsiders/unmapped users get standing access)
        and *masks* the intended model. The fix is fail-closed: no matching group → no role →
        access denied.
      - In Kanidm terms: scopes are granted via `oauth2 update-scope-map <client> <group>
        <scopes>` and claims via `update-claim-map`. A user in no mapped group ends up with no
        granted scopes — which is correct, *provided the RP treats "no scopes" as no access*
        rather than defaulting them in. Verify both sides.
      - Re-map upstream/external group claims through your own model (rules/02 §8); never let an
        upstream IdP's arbitrary group assertion directly grant a privileged local role.
      
      ```rego
      # GOOD (Rego): default deny; role derives only from an explicit group mapping
      package authz
      import rego.v1
      default allow := false
      role := r if { some g in input.user.groups; r := group_role[g] }   # undefined if no match
      allow if { some p in role_permissions[role]; p == input.action }
      # BAD: a fallback that silently grants everyone a baseline role
      role := group_role[g] if { some g in input.user.groups }
      role := "viewer"  # <-- default low role for ANY authenticated user regardless of group
      ```
      
      ## 4. Least privilege & segregation of duties
      
      - **Least privilege**: each role/grant carries the minimum actions on the minimum
        resources. No role grants estate-wide `*`/admin "to be safe." Scope by resource,
        environment, and tenant.
      - **Segregation of duties (SoD)**: define conflicting permission pairs that one identity
        must not hold together (create-vendor + approve-payment; request-access +
        approve-access; deploy-to-prod + approve-prod-deploy). Enforce SoD at grant time (block
        the assignment) and detect violations in access reviews. SoD violations are a Medium-to-
        High finding depending on the blast radius.
      - **Just-in-time elevation** rather than standing privilege (rules/05): a user requests a
        permission for a bounded window instead of holding it permanently.
      - **Log authorization decisions** (OWASP Authorization): the decision point emits a
        structured event — `who`, `action`, `resource`, `decision`, `policy/rule id` — for at
        least every *deny* and every privileged *allow*. Denies are a primary detection signal
        (enumeration, broken-object-level-auth probing); without them an authz bypass is
        invisible. Feed them to sota-detection-engineering; never log the token/credential.
      
      ## 5. Policy-as-code engines
      
      Author authorization as versioned, tested code — not as clicks in an admin UI:
      
      - **OPA / Rego** (CNCF Graduated): general policy engine; Rego is declarative and
        non-Turing-complete. Good for ABAC and centralized decision points; deploy as a sidecar
        or library; ship a bundle.
      - **AWS Cedar** (open-sourced 2023; powers Amazon Verified Permissions): purpose-built
        authorization language, deny-by-default, `forbid` overrides `permit` (encode hard
        ceilings as `forbid`), designed for analysis/formal reasoning.
      - **OpenFGA** (CNCF Incubating, late 2025) and **SpiceDB** (AuthZed): Zanzibar-style
        ReBAC. You declare a relationship schema and store tuples; the engine answers
        `check(user, relation, object)` and traverses inherited relationships. The model is the
        2019 Google Zanzibar paper. **Ory Keto** is another Zanzibar-style option.
      - Whatever the engine: a single decision API the app calls, policy in version control,
        reviewed and deployed like code, with the policy store itself protected (editing the
        policy is a privileged action — rules/05).
      
      ```cedar
      // Cedar: deny-by-default; forbid is a hard ceiling that overrides any permit
      permit (principal, action == Action::"invoice:read", resource)
        when { resource.tenant == principal.tenant &&
               (resource.owner == principal || principal in Role::"finance") };
      forbid (principal, action, resource)
        when { resource.classification == "restricted" && !principal.cleared };
      ```
      
      ```fga
      # OpenFGA: relationships; "viewer of a folder" inherits to documents in it
      model
        schema 1.1
      type document
        relations
          define parent: [folder]
          define viewer: [user] or viewer from parent
          define editor: [user]
      ```
      
      ## 6. Policy testing (an audit requirement, not a nicety)
      
      - Maintain an **allow/deny matrix** test suite: for each role/relationship × action ×
        resource-context, assert the expected decision, including the negatives (viewer cannot
        refund; cross-tenant denied; unmapped group → denied; SoD pair rejected).
      - Run policy tests in CI and gate merges. A change to a role definition or Rego/Cedar/FGA
        model without a corresponding test change is suspect.
      - Test the **fail-closed** behavior: engine timeout / lookup error must **deny**, never
        fall through to allow.
      
      ```python
      @pytest.mark.parametrize("groups,action,expected", [
          (["finance"],  "invoice:refund", True),
          (["finance"],  "invoice:delete", False),   # finance can't delete
          (["support"],  "invoice:refund", False),
          ([],           "invoice:read",   False),   # no mapped group -> DENY (no default role)
      ])
      def test_authz_matrix(groups, action, expected):
          assert decide(user(groups=groups), action) is expected
      ```
      
      ## Audit checklist
      
      - [ ] Is the authorization model chosen deliberately (RBAC/ABAC/ReBAC) and the simplest that expresses the rules?
      - [ ] Does the app authorize on permissions/relationships, not hardcoded role-name string checks?
      - [ ] Is there role explosion (combinatorial, unused, or context-encoding roles) that should be ABAC attributes?
      - [ ] On role/group change, are effective permissions recomputed (no accumulation/creep)?
      - [ ] Is birthright access minimal, with all privileged access requested + approved + expiring?
      - [ ] Is the group→role mapping explicit and **default-deny** — does an unmapped user get NO role rather than a silent default? (hunt for `default.*role`, `|| "viewer"`, fallback role assignment)
      - [ ] Are upstream/external group claims re-mapped through the local model, never trusted to grant privileged roles directly?
      - [ ] Does any role grant estate-wide `*`/admin without justification? (grep policy for `"*"`, `Action::"*"`, `allow.*true` without conditions)
      - [ ] Are segregation-of-duties conflicting pairs defined and enforced at grant time?
      - [ ] Is policy authored as versioned code (OPA/Cedar/OpenFGA/SpiceDB) with a single decision API, not clicked into a UI?
      - [ ] Is the policy store itself a protected/privileged resource?
      - [ ] Is there an allow/deny matrix test suite (including negatives and the unmapped-group case) gating CI?
      - [ ] Does the engine fail **closed** (deny) on timeout/error?
      
    • 04-lifecycle-provisioning.md 7.2 KB
      # 04 — Lifecycle & Provisioning
      
      Scope: the identity lifecycle — **Joiner-Mover-Leaver (JML)**, SCIM-driven
      provisioning *and* deprovisioning, the orphaned-account problem, access reviews /
      recertification, just-in-time (JIT) provisioning, and dormant-account detection.
      
      This is the **most-failed area in IAM**, and the failure is almost always the same:
      deprovisioning. Creating access is visible and self-correcting (people complain when they
      can't log in); removing access is invisible and silent (no one complains that a departed
      employee still has a token). Design deprovisioning *first*.
      
      ## 1. Source of truth and the JML model
      
      - There is **one authoritative source of identity** (HRIS for employees, a partner
        directory for B2B, a service registry for machines). Every account maps to a record in
        it. Access lifecycle events are *driven by* changes there, not entered ad hoc in each
        app.
      - **Joiner**: on hire, the source creates the identity; birthright access (baseline
        accounts/groups, minimal — rules/03) is provisioned automatically; everything beyond is
        requested + approved with an owner and expiry.
      - **Mover**: on role/department change, access is **recomputed**, not accumulated —
        old-role access is *removed* as new-role access is granted. Movers are where access
        creep hides: a long-tenured employee who changed teams five times and kept every grant.
      - **Leaver**: on termination, **all** access is revoked within a tight SLA — accounts
        disabled, sessions and tokens killed (rules/02 §6, rules/06 CAEP), API keys and
        workload credentials they own reassigned/rotated. Disable before delete (preserve audit
        trail), then delete/anonymize per retention policy (**sota-privacy-compliance**).
      
      ## 2. SCIM-driven provisioning AND deprovisioning
      
      - Use **SCIM 2.0** (RFC 7643/7644, protocol in rules/01 §8) so the source of truth pushes
        create/update/deactivate to every connected app, rather than each app managing its own
        user list.
      - **Deprovisioning is the half everyone forgets.** A SCIM `active=false` / DELETE on a
        leaver must:
        - terminate the account's ability to authenticate (not just hide it in the UI),
        - kill live sessions and revoke refresh tokens,
        - cascade to apps that don't speak SCIM (manual runbook with an SLA and a verification
          step).
      - Apps that *cannot* be SCIM-provisioned (no connector) are the orphan factory: maintain
        an explicit list, and a manual deprovisioning checklist that is *verified*, not assumed.
      - Verify deprovisioning end-to-end: a test that disables a test identity at the source and
        asserts it can no longer authenticate to each downstream app.
      
      ## 3. The orphaned-account problem (#1 IAM failure)
      
      An **orphaned account** has no valid owner — the human left, the service was
      decommissioned, the contract ended — but the account still authenticates. It is the
      prime target for takeover because no one watches it.
      
      - **Reconcile continuously**: periodically diff the IdP/app population against the source
        of truth. Every account with no matching active record is an orphan candidate → disable
        → investigate → delete.
      - High-risk orphans: **privileged** accounts of departed admins, **service accounts**
        whose owning team dissolved, **external/B2B** accounts past contract end, **break-glass**
        accounts (rules/05) that linger between uses.
      - Every account has a named **owner** (a person, not a team alias that no one reads). An
        ownerless account is itself a finding.
      
      ## 4. Access reviews / recertification
      
      - Periodically, the owner/manager **recertifies** that each grant is still needed.
        Cadence by risk: privileged and SoD-sensitive access quarterly (or tighter); standard
        access semi-annually/annually. This is a SOC 2 / ISO 27001 control —
        **sota-privacy-compliance** for evidence.
      - Reviews must be **actionable and default-revoke**: "review by the deadline or access is
        removed," not a rubber-stamp where everything is approved in bulk. Track decisions as
        audit evidence (who certified what, when).
      - Reviews surface: role explosion, access creep on movers, orphans, SoD violations,
        unused grants (granted but never exercised — candidates for removal).
      
      ## 5. Just-in-time (JIT) provisioning
      
      - **JIT account provisioning** (federation): on first SSO login from a trusted upstream,
        create the local account from verified token claims rather than pre-provisioning
        everyone. Useful for large B2B/social populations. Risks: deterministic linking on a
        verified immutable id (rules/02 §8) and a **deprovisioning** story — a JIT-created
        account still needs a leaver path (it won't get a SCIM DELETE if the upstream doesn't
        send one). Pair JIT-in with reconciliation/dormancy cleanup.
      - **JIT privilege elevation** (different thing): request elevated rights for a bounded
        window instead of holding them — covered in rules/05.
      
      ## 6. Dormant-account detection
      
      - Run a job that flags accounts with **no successful authentication in N days** (e.g. 30
        for privileged, 90 for standard). Dormant accounts are disabled after a grace/notice
        window; dormant *privileged* accounts are escalated immediately.
      - Dormancy detection requires reliable **last-login** telemetry — ensure the IdP emits
        auth events and they are retained (feeds **sota-detection-engineering** for anomaly and
        impossible-travel detection too).
      - Distinguish dormant *humans* (likely a missed leaver) from dormant *service accounts*
        (likely a decommissioned workload whose credential is now a standing liability —
        rules/05).
      
      ```sql
      -- Dormant human accounts (no login in 90d) that still authenticate
      SELECT a.username, a.last_login_at, a.owner, a.is_privileged
      FROM accounts a
      WHERE a.enabled
        AND a.type = 'human'
        AND (a.last_login_at IS NULL OR a.last_login_at < now() - interval '90 days')
      ORDER BY a.is_privileged DESC, a.last_login_at NULLS FIRST;
      ```
      
      ## Audit checklist
      
      - [ ] Is there a single authoritative source of identity, and does every account map to a record in it?
      - [ ] Is birthright access minimal and automatic, with all other access requested + approved + expiring?
      - [ ] On role change (mover), is access **recomputed** rather than accumulated? Look for long-tenured users with grants from old roles.
      - [ ] Is there a defined leaver SLA, and does termination revoke ALL access — disable account, kill sessions/refresh tokens, rotate owned credentials?
      - [ ] Is deprovisioning SCIM-driven where possible, with an explicit verified manual runbook for non-SCIM apps?
      - [ ] Does an end-to-end test confirm a disabled identity can no longer authenticate downstream?
      - [ ] Is there continuous reconciliation against the source of truth to find orphaned accounts? (no record → disable → delete)
      - [ ] Does every account — human, service, external, break-glass — have a named individual owner?
      - [ ] Are there orphaned privileged or service accounts of departed staff / dissolved teams / ended contracts? (highest priority)
      - [ ] Are access reviews/recertification run on a risk-based cadence, default-revoke, with decisions retained as audit evidence?
      - [ ] Do JIT-provisioned (federated) accounts have a working leaver/dormancy path, with linking on a verified immutable id?
      - [ ] Is there a dormant-account detection job (with reliable last-login telemetry) that disables stale accounts and escalates dormant privileged ones?
      
    • 05-privileged-workload.md 8.2 KB
      # 05 — Privileged Access & Workload Identity
      
      Scope: privileged access management (PAM) — admin-account separation, break-glass
      design, just-in-time / just-enough elevation, session recording, vaulting — and machine
      / workload identity — SPIFFE/SPIRE, workload identity federation, service-to-service
      auth, mTLS identity, short-lived over static credentials.
      
      Privileged identities and machine identities are where a single compromise becomes total
      compromise. The governing principle: **no standing privilege and no standing secret** —
      elevate just-in-time, authenticate workloads with short-lived federated credentials, and
      make every privileged action loud and auditable.
      
      For the *secret-storage* mechanics (Vault dynamic creds, OIDC-federation token exchange,
      `kid` rotation) see **sota-secrets-management** rules/01 and rules/05. For mTLS/ZTNA
      transport see **sota-network-security**. For K8s SA tokens / OIDC to the API server see
      **sota-kubernetes**. This file owns the *access-management design*.
      
      ## 1. Admin-account separation
      
      - **Admin work uses a separate identity** from daily work. The same human has a normal
        account (email, chat, browsing) and a distinct privileged account; the privileged
        account never reads email or browses the web (the phishing/drive-by surface that
        compromises admin rights).
      - Privileged accounts require **phishing-resistant MFA** (FIDO2/passkey — rules/06),
        shorter session caps (rules/02 §6), and ideally a dedicated admin workstation / PAW or
        identity-aware-proxy-gated access (**sota-network-security**).
      - No shared admin accounts. Every privileged action attributes to a named human (or a
        named workload). A shared `root`/`admin` login is a finding — break-glass excepted (§3).
      
      ## 2. Just-in-time / just-enough elevation
      
      - **No standing admin.** Default state: the human holds *no* privileged role. To perform
        privileged work they **request elevation** for a specific role/scope, for a bounded
        window, with approval and a reason; the grant auto-expires.
      - **Just-enough**: elevate to the *narrowest* role for the task (read-only break-fix vs
        full admin), not blanket superuser.
      - Elevation is logged with who/what/when/why/approved-by, and ideally requires a second
        approver for the highest tiers (pairs with SoD, rules/03 §4).
      - This is the standing-privilege fix for the access-creep and orphaned-admin problems in
        rules/04: there is simply far less standing privilege to leak or forget.
      
      ## 3. Break-glass (emergency access)
      
      Break-glass is the deliberate exception that must exist (the IdP/SSO is down, or the
      normal admin path is unavailable) — and it is precisely what attackers target, so it must
      be tightly controlled:
      
      - **Exists, but dormant.** A small number of emergency accounts that are *not* used for
        daily work and are normally disabled or credential-less.
      - **Logged, time-bound, alerted.** Any use of break-glass fires a **loud real-time alert**
        (it should be impossible to use one quietly), is time-boxed, and is fully audit-logged.
        An *un-alerted* break-glass account is indistinguishable from a backdoor — High finding.
      - **Strong credential, split if shared.** If a break-glass credential is a shared secret,
        vault it, split knowledge (no single person holds it), and rotate after every use.
      - **The Kanidm pattern**: Kanidm's `admin` and `idm_admin` are explicitly break-glass /
        disaster-recovery accounts — they exist for initial setup and recovery, *not* daily use,
        and are recovered out-of-band from the server host with
        `kanidmd recover-account admin` (or `idm_admin`), which mints a one-time recovery
        credential. Treat that command as a break-glass event: run it only in an emergency, on
        the server, and alert when it happens. Daily admin uses *separate* named accounts, never
        `admin`/`idm_admin`.
      
      ```
      # Break-glass usage on the Kanidm server host = an auditable emergency event
      kanidmd recover-account idm_admin     # generates a one-time recovery credential
      # After use: rotate, confirm the alert fired, log the incident, restore normal admin path.
      ```
      
      ## 4. Session recording & vaulting
      
      - For the highest tiers (production database admin, infra root, jump hosts), broker
        privileged sessions through a **PAM/bastion** that records the session (commands /
        keystrokes / screen) and brokers credentials so the human never holds the raw
        credential.
      - **Vault credentials, issue dynamically.** Privileged credentials (DB superuser, cloud
        admin) are not handed out long-lived; they are checked out for a session and revoked
        after — Vault/OpenBao dynamic secrets (**sota-secrets-management** rules/01/02).
      - Recordings and access logs are themselves sensitive and tamper-evident; protect and
        retain them per **sota-privacy-compliance**.
      
      ## 5. Machine / workload identity
      
      Workloads need identity too, and the failure mode is the **long-lived static secret** (an
      API key or service-account key file baked into config). Replace it:
      
      - **SPIFFE / SPIRE**: every workload gets a cryptographic identity (a SPIFFE ID like
        `spiffe://trust-domain/ns/app`) materialized as a short-lived SVID (X.509 cert or JWT),
        auto-rotated by the SPIRE agent based on platform attestation. Service-to-service auth
        is then mTLS with SVIDs — no shared secret. Coordinate with **sota-network-security**
        for the mTLS plane.
      - **Workload identity federation**: a workload (CI job, cloud function, pod) presents a
        platform-issued OIDC token and exchanges it (RFC 8693 token exchange) for a short-lived
        access token at the IdP/cloud — no stored credential at all. This is the preferred way
        for CI→cloud and service→cloud. The OIDC-federation *mechanics* are
        **sota-secrets-management** rules/01; the *identity design* (one workload identity per
        service, narrowly scoped, attested) is here.
      - **Short-lived over static, always.** A workload credential should live minutes, be
        scoped to one service's needs, and rotate automatically. A static service-account key
        in a repo/config/env is a defect — push it up the hierarchy to federation or SVIDs.
      - **One identity per workload**, scoped least-privilege through the authorization model
        (rules/03). Shared service accounts used by many services destroy attribution and blast-
        radius control.
      - Workload identities are in the JML/dormancy scope too (rules/04): a decommissioned
        service's identity must be retired, and dormant service credentials detected.
      
      ```
      # BAD: static, long-lived, broadly-scoped machine credential
      SVC_API_KEY="sk_live_8f3...permanent"        # in env/config, never rotates, shared
      
      # GOOD: short-lived federated/attested identity
      #   pod presents projected SA token -> exchanged for a 15m, single-service token
      #   or SPIRE issues an auto-rotating SVID; service-to-service is mTLS with the SVID
      ```
      
      ## Audit checklist
      
      - [ ] Do admins use a separate privileged identity from their daily account, with the privileged account barred from email/browsing?
      - [ ] Do privileged accounts require phishing-resistant MFA and shorter session caps?
      - [ ] Are there shared admin logins (non-break-glass)? Every privileged action should attribute to a named principal.
      - [ ] Is privileged access just-in-time (no standing admin) — requested, approved, reason-logged, auto-expiring?
      - [ ] Is elevation just-enough (narrowest role), with a second approver for the highest tiers?
      - [ ] Do break-glass accounts exist, stay dormant, and fire a loud real-time alert on every use?
      - [ ] Are break-glass uses time-boxed, fully logged, and the credential rotated after use (and split-knowledge if shared)?
      - [ ] For Kanidm: are `admin`/`idm_admin` treated as break-glass only (recovered via `kanidmd recover-account`), with daily admin on separate named accounts? Is `recover-account` use alerted?
      - [ ] Are highest-tier sessions brokered/recorded via a PAM/bastion, with dynamically-issued (not standing) privileged credentials?
      - [ ] Do workloads use short-lived federated/attested identity (SPIFFE/SPIRE SVIDs or workload identity federation) instead of static keys? Hunt config/env for long-lived `*_API_KEY`, `*-key.json`, service-account key files.
      - [ ] Is there one least-privilege identity per workload (no shared service accounts)?
      - [ ] Are decommissioned workload identities retired and dormant service credentials detected (rules/04)?
      
    • 06-mfa-federation-assurance.md 8.3 KB
      # 06 — MFA, Passwordless, Federation Risk & Assurance
      
      Scope: phishing-resistant MFA (FIDO2/passkeys/WebAuthn at the IdP), step-up / adaptive /
      conditional access, Continuous Access Evaluation (CAEP / Shared Signals Framework),
      push-bombing / MFA-fatigue defenses, B2B/B2C/social-login and account-linking risks, and
      identity proofing / assurance levels (NIST SP 800-63-4 IAL/AAL/FAL).
      
      This file owns the **IdP-side authentication strength and federation-risk posture**. The
      *WebAuthn ceremony implementation* at one RP (challenge generation, attestation handling,
      credential storage) is **sota-code-security** rules/02 — reference it for the wire-level
      ceremony; here we own the policy and the assurance model.
      
      ## 1. Phishing-resistant MFA at the IdP
      
      - **FIDO2 / WebAuthn / passkeys are the target state.** They are phishing-resistant:
        origin-bound (the credential only works for the registered relying-party origin),
        challenge-response, no shared secret to phish or replay. WebAuthn is at **Level 3**
        (W3C Candidate Recommendation as of early 2026 — current spec level, not yet a finished
        Recommendation).
      - **Passkeys** = FIDO credentials, either device-bound (hardware security key, platform
        authenticator) or **synced** multi-device (synced through a provider's keychain). Synced
        passkeys trade some assurance for huge usability/recovery wins — for the highest
        assurance prefer device-bound/hardware authenticators.
      - **MFA factor ranking** (use the strongest the population supports):
        1. FIDO2 hardware security key / device-bound passkey (phishing-resistant) — best.
        2. Synced passkey / platform authenticator (phishing-resistant).
        3. App-based push with number-matching (phishable but resists fatigue — §3).
        4. TOTP / authenticator-app codes (phishable via real-time relay).
        5. SMS / voice OTP — **not phishing-resistant** (SIM-swap, interception, relay). Treat
           as a Medium finding where phishing-resistant options are feasible; never the only
           factor for privileged accounts.
      - **Require phishing-resistant MFA for all privileged accounts** (rules/05) and drive all
        users toward passkeys. Enroll passkeys at the IdP and let them satisfy MFA across all
        federated RPs via SSO.
      
      ## 2. Step-up, adaptive & conditional access
      
      - **Step-up authentication**: low-risk actions ride the existing session; sensitive
        actions (change MFA, move money, export data, admin operations) demand a *fresh*
        strong authentication. Express the requirement as an assurance level (AAL) or ACR the
        RP requests and the IdP enforces (`acr_values` / `max_age` in the OIDC request).
      - **Adaptive / conditional access**: gate authentication on context — device posture,
        network/location, impossible-travel, risk score. High risk → step-up or block; low risk
        → allow. Feed the risk signals from and to **sota-detection-engineering** (auth anomaly,
        impossible-travel detections).
      - Conditional access is policy-as-code too: version it, test it, and fail closed (an
        unevaluated condition denies or steps up, never silently allows).
      
      ## 3. Push-bombing / MFA-fatigue defenses
      
      The attacker has the password and spams push prompts until the user taps "approve":
      
      - **Number matching** — the user types a number shown on the login screen into the app,
        so a blind "approve" cannot succeed.
      - **Rate-limit and lock out** repeated push prompts; alert on push storms.
      - **Show context** in the prompt (app, location, IP) so the user can spot the anomaly.
      - The real fix is **phishing-resistant MFA** (§1), which has no "approve" to spam.
      
      ## 4. B2B / B2C / social-login & account-linking risk
      
      - **Social / external login** delegates authentication to an upstream IdP. Validate its
        tokens fully (rules/01 §3), pin the issuer, and **only trust verified claims** (e.g.
        `email_verified=true`) — never link on an unverified email.
      - **Account-linking attacks**: linking a federated identity to a local account on a
        *mutable* or *unverified* attribute lets an attacker pre-register or collide and take
        over. Link deterministically on a **verified, immutable** identifier (provider `sub` +
        verified email); require re-verification to link a second IdP to an existing account.
      - **B2B federation**: each partner/tenant trust is a boundary (rules/02 §8). Re-map their
        group/role claims through your own authorization model (rules/03) — a partner IdP must
        not be able to assert your privileged roles.
      
      ## 5. Continuous Access Evaluation (CAEP / Shared Signals Framework)
      
      Bearer tokens are valid until they expire, so a revocation/disable does not take effect
      until the token times out — the gap that lets a just-fired employee keep working for the
      token lifetime. CAEP/SSF closes it:
      
      - **Shared Signals Framework (SSF) 1.0** is **final (29 August 2025)** at the OpenID
        Foundation — a transport framework for asynchronously delivering Security Event Tokens
        (SETs) between an IdP and RPs/receivers.
      - **CAEP 1.0** is **final (29 August 2025)** — defines the event types carried over SSF,
        including **session-revoked, credential-change, assurance-level-change** (plus
        token-claims-change, device-compliance-change, session-established/presented,
        risk-level-change).
      - Use it so that disabling an account, a credential change, or a risk-level rise **pushes
        a revocation event** to relying parties in near-real-time instead of waiting for token
        expiry. This is the propagation mechanism behind the leaver SLA (rules/04) and Single
        Logout (rules/02 §6). **RISC** is the parallel SSF profile for account-takeover/fraud
        signals.
      
      ## 6. Identity proofing & assurance levels (NIST SP 800-63-4)
      
      **NIST SP 800-63-4 "Digital Identity Guidelines" is final (July 2025)**, superseding
      Rev 3, across three volumes: 800-63A-4 (proofing), 800-63B-4 (authentication), 800-63C-4
      (federation). The assurance model:
      
      - **IAL — Identity Assurance Level**: confidence that the person is who they claim
        (identity proofing). IAL1→IAL2→IAL3 rising rigor.
      - **AAL — Authenticator Assurance Level**: confidence in the authentication
        (authenticator strength + binding). AAL2 needs MFA; **AAL3 requires a hardware-based,
        phishing-resistant authenticator**.
      - **FAL — Federation Assurance Level**: strength of the federated assertion (signing,
        encryption, holder-of-key binding). FAL rises with assertion protection.
      
      Match the assurance level to the risk of the resource (don't demand IAL3 in-person
      proofing to read a blog; do demand AAL3 for production infra). What 800-63-4 changed vs
      Rev 3, reflect these:
      - **Syncable authenticators (passkeys) are explicitly recognized** as an authenticator
        type.
      - **No periodic password rotation** and no arbitrary composition rules — rotate passwords
        only on evidence of compromise (the app-side storage of those passwords is
        **sota-code-security** rules/02).
      - Stronger emphasis on **phishing-resistant authenticators** for higher AAL / high-risk.
      
      ## Audit checklist
      
      - [ ] Is phishing-resistant MFA (FIDO2/passkey) available at the IdP and **required for all privileged accounts**?
      - [ ] Is SMS/voice OTP relied on as a sole or primary factor anywhere it could be phishing-resistant instead?
      - [ ] Are users actively driven toward passkeys, with enrollment at the IdP satisfying MFA across federated RPs?
      - [ ] Do sensitive operations require step-up (fresh strong auth via `acr_values`/`max_age`), not just an existing session?
      - [ ] Is conditional/adaptive access policy versioned, tested, and fail-closed?
      - [ ] Are push-MFA prompts protected with number-matching, rate limiting, context display, and storm alerting?
      - [ ] Does social/external login trust only verified immutable claims, with the upstream issuer pinned and tokens fully validated?
      - [ ] Is account linking done on a verified immutable id, with re-verification to add a second IdP (no linking on mutable/unverified email)?
      - [ ] Are B2B partner group/role claims re-mapped through the local authorization model, never trusted to grant privileged roles?
      - [ ] Is CAEP/SSF (or an equivalent) wired so disable/credential-change/risk events propagate revocation to RPs in near-real-time, not at token expiry?
      - [ ] Are IAL/AAL/FAL levels chosen to match resource risk, with AAL3 (hardware phishing-resistant) for the highest-risk access?
      - [ ] Is password policy 800-63-4-aligned (no forced periodic rotation, no composition rules; rotate on compromise only)?
      
    • 07-active-directory.md 16.4 KB
      # 07 — Active Directory, Kerberos & ADCS Hardening
      
      Scope: hardening and secure design of on-premises **Active Directory Domain
      Services (AD DS)**, the **Kerberos** and **NTLM** authentication planes, **Active
      Directory Certificate Services (ADCS)**, and the credential-protection controls
      that blunt lateral movement and domain-dominance attacks — plus the boundary
      where AD meets **Entra ID** in a hybrid estate.
      
      This file owns the **preventive** posture (build it so the attack can't land).
      The **detective** posture — which events to collect, and the Sigma-style logic
      that catches Kerberoasting, DCSync, golden tickets, ADCS abuse, and RBCD writes
      — lives in **sota-detection-engineering rules/07 (AD attack detection)**. Harden
      here; detect there. For app-level login/session mechanics see
      **sota-code-security**; for the identity *model* (JML, least privilege, MFA) see
      rules/03–06 of this skill.
      
      AD is the classic **tier-0 asset**: a single Domain Admin or `krbtgt` compromise
      is total, durable, and hard to evict. Design assuming an attacker already holds a
      low-privileged domain account — that is the realistic starting position.
      
      ## 1. Enterprise Access Model & tiering
      
      - The legacy **ESAE / "red forest"** (a dedicated hardened administrative forest)
        was **retired as Microsoft's default recommendation in December 2020** and
        replaced by the **modern privileged-access strategy** and the **Enterprise
        Access Model (EAM)** (verify: `learn.microsoft.com/security/privileged-access-workstations/esae-retirement`).
        Do not stand up a new red forest as a first move; existing ones can remain but
        are no longer the recommended pattern.
      - The EAM generalizes the old three-tier model (Tier 0/1/2) into **access
        planes** — control, management, and data/workload — and folds in cloud/Entra
        and user/app access. **Tier 0 = anything that can control AD identity**: DCs,
        `krbtgt`, Domain/Enterprise Admins, ADCS CAs, AD-integrated DNS, sync servers,
        and any account or host that can gain those. The core rule survives the
        rename: **a higher tier never authenticates to (or exposes its credential on) a
        lower-tier host.**
      - **Clean source / no credential exposure downhill.** A Tier-0 admin never logs
        on interactively to a workstation or member server (that credential can be
        harvested from LSASS). Administer tier-0 from **Privileged Access Workstations
        (PAWs)** and enforce logon isolation with **authentication policies and silos**
        (§4) plus `Deny log on` User-Rights restrictions per tier.
      - **Separate admin identities per plane** (rules/05): a human has a normal
        account and distinct privileged account(s); privileged accounts never read
        email or browse. Enforce **just-in-time** elevation, not standing membership in
        Domain Admins.
      - Adopt a **least-privilege delegation model** (OU-scoped delegated rights)
        instead of dumping helpdesk/staff into built-in privileged groups. Tools such
        as BloodHound or PingCastle (neutral examples) surface the *attack paths* — the
        transitive edges from a low-priv account to Tier 0 — that this model must cut.
      
      ## 2. Kerberos & NTLM hardening
      
      ### Delegation (the highest-value misconfig class)
      
      - **Unconstrained delegation** caches the *TGT* of any principal that
        authenticates to the host, so a compromise of that host (or coercing a DC to
        authenticate to it) yields a DC TGT → domain compromise. **Eliminate it.** No
        account should carry `TRUSTED_FOR_DELEGATION` except, unavoidably, DCs. Audit
        `userAccountControl` for the flag across users and computers.
      - **Constrained delegation** (`msDS-AllowedToDelegateTo`) limits which SPNs a
        host may impersonate to — better, but **protocol transition**
        (`TRUSTED_TO_AUTH_FOR_DELEGATION`, "any authentication") lets the host mint
        tickets for arbitrary users to those services; scope it tightly and prefer
        Kerberos-only (`use-any-protocol` off).
      - **Resource-based constrained delegation (RBCD)** moves the trust to the
        *target* (`msDS-AllowedToActOnBehalfOfOtherIdentity`). It is the cleanest model
        *and* a favourite escalation primitive: an attacker who can **write that
        attribute** on a computer object (often combined with a controlled machine
        account) impersonates any user to it. Restrict who can write it; the write
        itself is a key detection signal (detection rules/07, event 5136).
      - Mark truly sensitive accounts **`Account is sensitive and cannot be
        delegated`** (or add them to **Protected Users**, §4) so no delegation can
        impersonate them.
      
      ### Service accounts, Kerberoasting & AS-REP roasting
      
      - **Kerberoasting** (ATT&CK **T1558.003**): any domain user can request a TGS for
        any SPN; if it is encrypted with **RC4 (etype 23)** the reply is offline-
        crackable to the service account's password. Mitigations, in order:
        - **Managed service accounts.** **gMSA** (group Managed Service Account) and,
          in **Windows Server 2025, dMSA** (delegated Managed Service Account) give
          accounts **fully randomized, auto-rotated 120-character keys** that are not
          crackable, and dMSA additionally **binds authentication to device identity**
          and disables the migrated account's old password (verify:
          `learn.microsoft.com/windows-server/identity/ad-ds/manage/delegated-managed-service-accounts/delegated-managed-service-accounts-overview`).
          Prefer these over human-set service passwords wherever the service supports
          them. *Caveat:* dMSA has published abuse research — **BadSuccessor**
          (privilege escalation via dMSA migration; patched as **CVE-2025-53779**,
          Aug 2025 — the KDC now requires a bidirectional dMSA↔superseded-account
          link) and **Golden dMSA** (key derivation). Post-patch variants still abuse
          over-permissive dMSA rights, so restrict dMSA creation/migration to tier-0
          and monitor it (detection rules/07).
        - **AES-only service accounts.** Set `msDS-SupportedEncryptionTypes` to
          AES128/256 (etype 17/18) and disable RC4 so the roast target is far more
          expensive; do this estate-wide only after confirming no RC4 dependency.
        - For any remaining human-managed service account: a **long random password
          (25+ chars)** and rotation, and **minimum SPNs** (SPN hygiene — remove stale/
          duplicate SPNs; never put an SPN on a high-privilege user, which turns a
          Domain Admin into a roastable target).
      - **AS-REP roasting** (ATT&CK **T1558.004**): accounts with **Kerberos
        pre-authentication disabled** (`DONT_REQ_PREAUTH`) hand out an offline-crackable
        AS-REP to any unauthenticated requester. **Require pre-auth on every account**;
        the flag should appear nowhere.
      
      ### NTLM, relay & machine-account quota
      
      - **Prefer Kerberos; retire NTLM.** Audit NTLM use (NTLM Operational log,
        event 8004 — detection rules/07) then restrict it with the **Network Security:
        Restrict NTLM** policies; NTLMv1 must be disabled outright.
      - **Relay mitigations.** Enforce **SMB signing** and **LDAP signing + channel
        binding (EPA)** so coerced authentications can't be relayed. Windows Server 2025
        hardens the defaults: **SMB signing is required by default** (also on Windows 11
        24H2), and new DCs default to **requiring LDAP signing** (new "LDAP server
        signing requirements Enforcement" policy); **LDAP channel binding still defaults
        to *When supported*, not *Required***, so set it to Always where clients allow
        (verify: `techcommunity.microsoft.com` LDAP-signing Server-2025 post and
        `learn.microsoft.com/windows-server/identity/ad-ds/ldap-signing`). Disable/patch
        coercion surfaces (PetitPotam-class RPC).
      - **Machine-account quota.** `ms-DS-MachineAccountQuota` **defaults to 10**,
        letting *any* authenticated user join up to ten computer accounts — the fuel for
        RBCD and several escalation chains. **Set it to 0** and grant machine-join via a
        delegated right instead.
      
      ## 3. Active Directory Certificate Services (ADCS)
      
      ADCS is a tier-0 system: a CA that issues an authentication certificate for an
      arbitrary principal is a domain-compromise primitive. The escalation classes were
      catalogued by **SpecterOps ("Certified Pre-Owned", Schroeder & Christensen,
      2021)** — originally **ESC1–ESC8** — and **community-extended through ~ESC16 as
      of 2025** (Certipy/Oliver Lyak, TrustedSec; verify the current set against
      SpecterOps' publications and the Certify/Certipy docs before citing a specific
      number).
      
      - **Template hardening (ESC1–ESC4).** For any template with an authentication EKU
        (Client Auth / Smart Card Logon / PKINIT / *Any Purpose*):
        - **Never allow enrollee-supplied subject** (`CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT`)
          on an auth template — that is **ESC1** (attacker names an arbitrary SAN/UPN).
        - Require **manager approval** and/or **authorized-signature (enrollment agent)**
          for sensitive templates; don't grant broad **Enroll**/**AutoEnroll** to
          `Domain Users`/`Authenticated Users`.
        - Lock down **template and CA ACLs** — write access to a template is **ESC4**
          (rewrite it into ESC1); dangerous CA flags (`EDITF_ATTRIBUTESUBJECTALTNAME2`)
          are **ESC6**.
      - **Web enrollment / relay (ESC8, ESC11).** The HTTP enrollment endpoints accept
        relayed NTLM → cert for a DC. Disable web enrollment if unused; otherwise
        enforce **HTTPS + EPA** and the NTLM-relay controls in §2.
      - **Enrollment-agent restrictions.** Enrollment-agent certificates let the holder
        enroll *on behalf of* others — restrict which agents, templates, and target
        principals are permitted (CA "Enrollment Agents" tab), or the agent becomes a
        domain-wide impersonation tool.
      - **Strong certificate mapping (KB5014754).** The May 2022 update **KB5014754**
        adds a **SID extension** to issued certs and makes DCs enforce **strong
        certificate mapping**, closing the weak implicit-mapping (UPN/SAN) abuse.
        Enforcement timeline (verify at `support.microsoft.com` KB5014754): DCs moved to
        **Full Enforcement with the February 11, 2025 update**, and the ability to fall
        back to Compatibility mode was **removed after the September 9, 2025 update**.
        Ensure CAs embed the SID extension and that no weak `altSecurityIdentities`
        mappings remain.
      - **Protect the CA private key** (HSM), restrict CA administration to tier-0, and
        monitor issuance (detection rules/07, events 4886/4887).
      
      ## 4. Credential protection
      
      - **Windows LAPS** randomizes and rotates the **local administrator password**
        per machine, killing pass-the-hash lateral movement across identical local
        creds. **Legacy Microsoft LAPS (the MSI) is deprecated as of Windows 11 23H2**;
        use the **built-in Windows LAPS** (Windows Server 2019+ / Win10+), which adds
        **password encryption, history, DSRM-password management, and Entra ID support**
        (verify: `learn.microsoft.com/windows-server/identity/laps/laps-overview`).
      - **Protected Users** group: members get hardened Kerberos (no RC4/DES, no NTLM,
        no unconstrained/constrained delegation, no long-lived TGT caching). Put
        **high-value human admins** in it — but not service accounts or accounts that
        legitimately need NTLM, and never the break-glass accounts you might need when
        Kerberos is broken (rules/05).
      - **Credential Guard** (VBS-isolated LSA) stops LSASS secret theft
        (pass-the-hash/ticket harvesting) on supported hosts; enable on admin
        workstations and, where compatible, member servers.
      - **Authentication policies & silos** bind privileged accounts so their TGTs are
        only usable from designated (PAW/tier-0) hosts and cap TGT lifetime — enforcing
        the clean-source rule of §1 in Kerberos itself.
      - **`krbtgt` rotation.** The `krbtgt` hash signs every TGT; its theft enables
        **golden tickets** (durable forgery of any identity). Rotate the `krbtgt`
        password **twice** (to also invalidate the *previous* key, which stays valid one
        cycle), **spaced by more than the maximum ticket lifetime** to avoid breakage —
        on a regular cadence (commonly ~every 6–12 months) and **immediately, twice, on
        any suspected DC/tier-0 compromise**. A never-rotated `krbtgt` is a finding.
        (Same twice-rotation logic applies to a compromised DSRM or trust key.)
      
      ## 5. Hybrid / Entra ID boundary
      
      Neutral trade-off notes — the goal is to keep an on-prem compromise from becoming
      a cloud compromise (and vice-versa):
      
      - **The sync engine is tier-0.** The directory-sync connector server and its
        **sync/connector account** hold broad read (and, with password-writeback or
        hybrid-join, write) over AD and Entra. Treat that host and account as tier-0;
        compromise of it bridges both directories. Do not let cloud-only admins reduce
        on-prem tiering, or vice-versa.
      - **PHS vs PTA (neutral).** **Password Hash Sync (PHS)** syncs a hash-of-a-hash to
        Entra — authentication survives on-prem outages and enables leaked-credential
        detection, at the cost of a derived secret residing in the cloud.
        **Pass-Through Authentication (PTA)** validates against on-prem DCs (no synced
        secret) but introduces the **PTA agent** as an on-prem authentication component
        whose compromise can intercept validations. **Federation (ADFS)** hands the
        entire token-issuance trust to an on-prem STS — a golden-SAML target — and is
        generally the heaviest to secure. Pick per outage-tolerance and threat model;
        document the choice.
      - **Retire legacy cloud-side auth paths.** Eliminate remaining **legacy Azure AD
        Graph API** dependencies (migrate to Microsoft Graph) and alert on anomalous
        Graph/actor-token activity: **CVE-2025-55241** (CVSS 10.0; reported and fixed
        Jul 2025, disclosed Sep 2025) let an attacker mint an undocumented **Actor
        token** in their own tenant and impersonate any user — including Global
        Admins — in *any* other tenant via Azure AD Graph, bypassing MFA, Conditional
        Access, and most logging. IdP-internal legacy auth paths can bypass every
        conditional control; treat them as tier-0 attack surface.
      - **Cloud Kerberos / hybrid join**: where AD and Entra co-issue, the tier-0
        boundary now spans both control planes — apply the higher bar of the two, and
        keep privileged cloud roles (Global Admin) on separate phishing-resistant
        accounts (rules/06).
      
      ## Audit checklist
      
      - [ ] Is a **tier-0 / Enterprise Access Model** boundary defined and enforced (DCs, `krbtgt`, Domain/Enterprise Admins, ADCS CAs, sync servers), with **no higher-tier credential exposed on a lower-tier host** (PAWs + `Deny log on` + auth silos)?
      - [ ] Any **unconstrained delegation** outside DCs? (Hunt `userAccountControl` for `TRUSTED_FOR_DELEGATION` — should be DCs only.)
      - [ ] Is **constrained delegation** tightly scoped, protocol-transition avoided, and **who can write `msDS-AllowedToActOnBehalfOfOtherIdentity` (RBCD)** restricted?
      - [ ] Are sensitive/admin accounts marked **`Account is sensitive and cannot be delegated`** or in **Protected Users**?
      - [ ] Do service accounts use **gMSA/dMSA** (randomized keys) or, failing that, **AES-only** encryption and 25+ char rotated passwords? Are there **SPNs on privileged user accounts** (Kerberoast bait) or stale/duplicate SPNs?
      - [ ] Any account with **Kerberos pre-auth disabled** (`DONT_REQ_PREAUTH`, AS-REP roastable)? Any **RC4 (etype 23)** still permitted where AES is feasible?
      - [ ] Is **NTLM audited and restricted** (NTLMv1 disabled), and are **SMB signing** and **LDAP signing + channel binding (EPA)** enforced (Server 2025 defaults confirmed, channel binding set beyond *When supported* where possible)?
      - [ ] Is **`ms-DS-MachineAccountQuota` set to 0** (default 10)?
      - [ ] ADCS: any **auth template allowing enrollee-supplied subject** (ESC1), broad **Enroll** to Domain/Authenticated Users, weak **template/CA ACLs** (ESC4), `EDITF_ATTRIBUTESUBJECTALTNAME2` (ESC6), or exposed **web enrollment without HTTPS+EPA** (ESC8)?
      - [ ] Are **enrollment agents restricted** by template/target, and is the **CA key HSM-protected** and CA admin tier-0?
      - [ ] Is **strong certificate mapping (KB5014754)** enforced — SID extension embedded, Full Enforcement in effect (post-Feb/Sep-2025 timeline), no weak `altSecurityIdentities` mappings?
      - [ ] Is **Windows LAPS** (not the deprecated legacy MSI) deployed to randomize/rotate local admin passwords, with encryption + history?
      - [ ] Are **Credential Guard** and **authentication policies/silos** enabled for privileged accounts/hosts?
      - [ ] Is **`krbtgt` rotated on a cadence and twice on suspected compromise** (rotations spaced beyond max ticket lifetime)? A never-rotated `krbtgt` is a finding.
      - [ ] Is the **directory-sync host + connector account treated as tier-0**, and is the **PHS/PTA/federation** choice deliberate, documented, and consistent with the tiering model?
      - [ ] Any remaining **legacy Azure AD Graph API** dependencies, and is anomalous **Graph/actor-token** activity alerted on (CVE-2025-55241 class)?
      
  • SKILL.md 12.2 KB
    ---
    name: sota-identity-access
    description: >-
      State-of-the-art identity and access management engineering (2026) for
      BUILDING/configuring identity infrastructure AND AUDITING it — federation,
      IdPs, authorization models, the joiner-mover-leaver lifecycle,
      privileged/break-glass access, workload identity, MFA/passkeys, and
      assurance levels; audits cover orphaned accounts, over-privileged roles,
      weak MFA, long-lived tokens, and SAML misconfig. Owns identity
      INFRASTRUCTURE and access-management DESIGN, not app-level login/session/JWT
      mechanics (sota-code-security owns those). Trigger keywords: IAM, IdP, OIDC,
      OAuth 2.1, PKCE, DPoP, PAR, RAR, JAR, FAPI, SAML,
      SCIM, provisioning, deprovisioning, RBAC, ABAC, ReBAC, Zanzibar, OpenFGA,
      SpiceDB, OPA, Cedar, Kanidm, Keycloak, Authentik, Zitadel, Entra, Okta, SSO,
      single logout, MFA, passkey, FIDO2, WebAuthn, step-up, conditional access,
      CAEP, PAM, break-glass, just-in-time, SPIFFE, access review, NIST 800-63,
      IAL, AAL, FAL, Active Directory, Kerberos, Kerberoasting, ADCS, RBCD,
      gMSA, dMSA, LAPS, krbtgt.
    ---
    
    # SOTA Identity & Access
    
    ## Purpose
    
    Own the identity **infrastructure** and access-management **design** of a system:
    the federation protocols themselves, the IdP that issues and validates tokens, the
    authorization model that decides who may do what, the lifecycle that creates and
    destroys access, privileged access, and machine identity. Two modes. In **BUILD**
    mode you stand up or configure this infrastructure correctly by default. In **AUDIT**
    mode you assess an existing identity estate against the same rules and report
    severity-rated findings. The rules files are the single source of truth for both.
    
    Boundary discipline — this skill does **not** re-teach what siblings own:
    - **App-level authn ceremony** (password storage/argon2id, session cookie flags,
      WebAuthn ceremony, JWT *signature* validation mechanics at one RP): that is
      **sota-code-security** rules/02. This skill owns the protocol and the IdP side.
    - **App-level object/function authz** (IDOR/BOLA in one service's handlers): that is
      **sota-code-security** rules/03. This skill owns the authorization *model* and the
      *policy engine* that the app calls.
    - **Secret storage, OIDC-federation mechanics for workloads, JWT `kid` rotation as a
      credential operation**: **sota-secrets-management** rules/01 and rules/05.
    
    Concurrent siblings to invoke alongside: **sota-network-security** (mTLS, ZTNA,
    identity-aware proxy), **sota-kubernetes** (K8s RBAC, OIDC to the API server, SA
    tokens), **sota-detection-engineering** (identity-based detections, impossible-travel,
    auth anomaly), **sota-privacy-compliance** (consent, DSAR, audit evidence).
    
    The hierarchy of preference, always: **(1)** no standing credential — short-lived,
    federated, sender-constrained tokens; **(2)** standing identity with strong
    phishing-resistant authentication and just-in-time elevation; **(3)** long-lived
    secret-authenticated client with rotation and audit; **(4)** anything static and
    broadly-scoped is a defect to be justified or removed.
    
    ## BUILD mode
    
    Use when standing up or configuring any identity component.
    
    1. **Pick the protocol, not the vibe.** Interactive user login → OIDC Authorization
       Code + PKCE (the only sanctioned interactive flow). Service-to-service → client
       credentials with `private_key_jwt`/mTLS, or workload identity federation. High
       assurance → FAPI 2.0. Legacy SAML only where a relying party requires it. Read
       `rules/01-federation-protocols.md` before configuring any client.
    2. **Treat the IdP as a tier-0 asset.** HA, backups of the identity store, restricted
       admin plane, signing-key rotation, break-glass design. `rules/02-idp-operations.md`.
    3. **Design the authorization model deliberately.** RBAC vs ABAC vs ReBAC is an
       architecture decision; model roles/relationships and write policy as code with a
       test matrix. `rules/03-authorization-models.md`.
    4. **Wire the lifecycle before launch.** Joiner-mover-leaver, SCIM provisioning AND
       deprovisioning, access reviews. Deprovisioning is the #1 IAM failure — design it
       first. `rules/04-lifecycle-provisioning.md`.
    5. **Separate and time-box privilege.** Admin-account separation, JIT elevation,
       logged-and-alerted break-glass, machine identity. `rules/05-privileged-workload.md`.
    6. **Make authentication phishing-resistant and adaptive.** Passkeys/FIDO2 at the IdP,
       step-up, CAEP/SSF for continuous evaluation. `rules/06-mfa-federation-assurance.md`.
    7. **Self-review against each file's Audit checklist** before declaring done.
    
    ## AUDIT mode
    
    Use when assessing an existing identity estate.
    
    ### Sweep procedure
    
    1. **Enumerate the IdP config**: clients/relying parties and their redirect URIs,
       client-auth methods, token lifetimes, grant types enabled, signing keys + rotation,
       session/SLO config, MFA policy, federation/brokering trusts. Pull from the IdP API
       or config export, not screenshots.
    2. **Enumerate the population**: every human and service account, its
       authentication strength, last-login, group/role assignments, and owner. Cross
       against the HR/source-of-truth roster to find orphans.
    3. **Sweep by rules file**: 01 (protocol/token misconfig), 02 (IdP hardening),
       03 (over-privilege/SoD), 04 (orphaned/dormant/no-reviews — usually the most
       findings), 05 (break-glass/standing admin/static workload creds), 06 (weak MFA).
    4. **Verify, don't assume**: a wildcard redirect URI, an account that logged in 400
       days ago, a role granting `*` — confirm each against the live config/logs before
       reporting. Never authenticate as a discovered account or trigger break-glass
       without explicit permission.
    
    ### Severity conventions
    
    | Severity | Definition | Examples |
    |---|---|---|
    | **Critical** | Identity-layer flaw enabling full account/tenant takeover or auth bypass for many principals | Wildcard/loose `redirect_uri` enabling token theft; IdP accepts unsigned SAML assertions or `alg:none`; standing super-admin with no MFA; signing key never rotated and leaked; OIDC issued to an open-redirect client |
    | **High** | Compromise of a single privileged identity, or systemic over-grant | Orphaned admin account still active post-termination; break-glass account with a static shared password and no alerting; role granting estate-wide `*`; long-lived non-rotating refresh tokens; SSO with no Single Logout on credential change |
    | **Medium** | Weak lifecycle/assurance on a contained scope | No access reviews/recertification; dormant non-priv accounts; phishable MFA (SMS/TOTP) where phishing-resistant is feasible; `client_secret_basic` where `private_key_jwt`/mTLS is supported; missing SoD on sensitive role pairs |
    | **Low** | Hygiene and defense-in-depth gaps | No idle session timeout; consent screen not informative; no dormant-account detection job; PAR/DPoP available but unused for a low-risk client; missing `azp` validation on a single-audience token |
    | **Info** | Observations and accepted risk | Legacy SAML RP documented and owner-acknowledged; planned migration off SMS MFA tracked |
    
    ### Finding format
    
    Report every finding as one line, ordered Critical → Info:
    
    ```
    file:line | rule | severity | effort (trivial/small/medium/large) | fix
    ```
    
    Where `file:line` anchors to the offending config (e.g. `keycloak/realm.json:412`,
    `policies/rbac.rego:88`, or `idp://clients/web-app#redirect_uris` for live config with
    no file). `rule` is the rules-file section (e.g. `01 §redirect-uri`). Group repeated
    instances of one weakness into a single finding listing all locations. End the audit
    with: counts per severity, the orphaned/dormant account tally, and the top 3 systemic
    fixes (almost always: deprovisioning automation, MFA hardening, least-privilege roles).
    
    ## Rules index
    
    | File | Read this when... |
    |---|---|
    | [rules/01-federation-protocols.md](rules/01-federation-protocols.md) | Configuring or auditing OIDC/OAuth flows, choosing a grant type, validating tokens at the RP, PKCE/PAR/RAR/JAR/DPoP, OAuth 2.1 & FAPI 2.0, SAML and its attack classes (XSW, comment injection, unsigned assertions), SCIM as a protocol, redirect-URI matching, token-validation pitfalls |
    | [rules/02-idp-operations.md](rules/02-idp-operations.md) | Running a self-hosted IdP (Kanidm/Keycloak/Authentik/Zitadel), client/RP registration discipline, client-auth ladder, token lifetimes + refresh rotation + reuse detection, signing-key (`kid`) rotation, session management + Single Logout, consent, multi-IdP brokering, IdP as tier-0 (HA/backup) |
    | [rules/03-authorization-models.md](rules/03-authorization-models.md) | Choosing/designing RBAC vs ABAC vs ReBAC, role modeling and role explosion, the group→role mapping discipline, least privilege + segregation of duties, policy-as-code engines (OPA/Rego, Cedar, OpenFGA, SpiceDB), policy testing, birthright vs requested access |
    | [rules/04-lifecycle-provisioning.md](rules/04-lifecycle-provisioning.md) | Designing or auditing joiner-mover-leaver, SCIM-driven provisioning/deprovisioning, the orphaned-account problem, access reviews/recertification, just-in-time provisioning, dormant-account detection |
    | [rules/05-privileged-workload.md](rules/05-privileged-workload.md) | Admin-account separation, break-glass design (logged/time-bound/alerted, the Kanidm `recover-account` pattern), JIT/just-enough elevation, session recording, vaulting; machine/workload identity (SPIFFE/SPIRE, workload identity federation, mTLS identity, short-lived over static) |
    | [rules/06-mfa-federation-assurance.md](rules/06-mfa-federation-assurance.md) | Phishing-resistant MFA (FIDO2/passkeys/WebAuthn at the IdP), step-up/adaptive/conditional access, CAEP/SSF continuous evaluation, push-bombing/MFA-fatigue defenses, B2B/B2C/social-login and account-linking risks, identity proofing and NIST 800-63-4 IAL/AAL/FAL |
    | [rules/07-active-directory.md](rules/07-active-directory.md) | Hardening on-prem Active Directory / Kerberos / ADCS: Enterprise Access Model & tiering (ESAE/red-forest retired), delegation risks (unconstrained/constrained/RBCD), Kerberoasting/AS-REP + gMSA/dMSA & AES-only service accounts, SPN hygiene, machine-account quota, NTLM relay (SMB/LDAP signing + channel binding), ADCS ESC classes + template/enrollment-agent hardening + strong cert mapping (KB5014754), LAPS/Protected Users/Credential Guard/auth silos, krbtgt rotation, hybrid Entra sync boundary (PHS/PTA). Detection lives in sota-detection-engineering rules/07 |
    
    ## Top-10 non-negotiables
    
    Violations are findings regardless of context; in BUILD mode they are never shortcuts.
    
    1. **Authorization Code + PKCE is the only sanctioned interactive flow.** Implicit and
       ROPC/password grant are dead and disabled at the IdP. (rules/01)
    2. **Exact redirect-URI matching, no wildcards, no scheme/host/path looseness.** A loose
       `redirect_uri` is a token-exfiltration primitive. (rules/01)
    3. **At the RP, pin algorithms and validate `iss`, `aud`, `exp`, and `nonce`; reject
       unsigned tokens and `alg:none`.** SAML RPs reject unsigned assertions and validate
       the signature over the whole response with anti-XSW canonicalization. (rules/01)
    4. **The IdP is a tier-0 asset**: HA, backed-up identity store, restricted admin plane,
       rotating signing keys with `kid` overlap, no standing super-admin without
       phishing-resistant MFA. (rules/02)
    5. **Refresh tokens rotate with reuse detection, or are sender-constrained (DPoP/mTLS);
       access tokens are short-lived.** No non-expiring tokens. (rules/01, rules/02)
    6. **Authorization is least-privilege by an explicit model with policy-as-code and a
       tested allow/deny matrix.** No role grants estate-wide `*`; segregation of duties
       enforced on sensitive pairs. (rules/03)
    7. **Group→role mapping is explicit and default-deny**: a user with no matching mapping
       gets *no* access, never a silent default role. (rules/03)
    8. **Deprovisioning is automated and prompt** — a leaver loses all access within the
       agreed SLA, source-of-truth driven via SCIM; access is recertified on a schedule.
       Deprovisioning is the #1 IAM failure. (rules/04)
    9. **Privileged access is separated, just-in-time, and time-boxed; break-glass is
       logged, alerted, and expires.** No permanent quiet admin backdoor. (rules/05)
    10. **Phishing-resistant MFA (FIDO2/passkeys) at the IdP for all privileged and,
        ideally, all users**; step-up for sensitive operations; SMS/voice OTP is not
        phishing-resistant. (rules/06)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related