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
Install
npx skills add https://github.com/martinholovsky/SOTA-skills/tree/main/skills/sota-identity-access
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install martinholovsky-sota-skills@llmmart
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
kidrotation 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.
- 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. Readrules/01-federation-protocols.mdbefore configuring any client. - 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. - 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. - 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. - Separate and time-box privilege. Admin-account separation, JIT elevation,
logged-and-alerted break-glass, machine identity.
rules/05-privileged-workload.md. - Make authentication phishing-resistant and adaptive. Passkeys/FIDO2 at the IdP,
step-up, CAEP/SSF for continuous evaluation.
rules/06-mfa-federation-assurance.md. - Self-review against each file's Audit checklist before declaring done.
AUDIT mode
Use when assessing an existing identity estate.
Sweep procedure
- 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.
- 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.
- 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).
- 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.
- Authorization Code + PKCE is the only sanctioned interactive flow. Implicit and ROPC/password grant are dead and disabled at the IdP. (rules/01)
- Exact redirect-URI matching, no wildcards, no scheme/host/path looseness. A loose
redirect_uriis a token-exfiltration primitive. (rules/01) - At the RP, pin algorithms and validate
iss,aud,exp, andnonce; reject unsigned tokens andalg:none. SAML RPs reject unsigned assertions and validate the signature over the whole response with anti-XSW canonicalization. (rules/01) - The IdP is a tier-0 asset: HA, backed-up identity store, restricted admin plane,
rotating signing keys with
kidoverlap, no standing super-admin without phishing-resistant MFA. (rules/02) - 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)
- 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) - Group→role mapping is explicit and default-deny: a user with no matching mapping gets no access, never a silent default role. (rules/03)
- 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)
- Privileged access is separated, just-in-time, and time-boxed; break-glass is logged, alerted, and expires. No permanent quiet admin backdoor. (rules/05)
- 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.
Reviews (0)
No reviews yet.
No comments yet.