Security

Threat Model

Security analysis and design decisions.

Threat Model

Email as Public Input

The user's email is entered at the SP to trigger DNS discovery. This is inherently visible to the SP β€” an accepted, unclosable information surface.

Why it's OK: The email is needed for DNS resolution (_ddisa.{domain}). Passkeys ensure that knowing someone's email is insufficient to impersonate them. This is the same model as email itself β€” knowing an address doesn't give you access.

DNS Discovery Trust Model

DDISA uses DNS TXT records (_ddisa.{domain}) to discover which IdP is authoritative for a given email domain. This makes DNS a critical part of the trust chain. Here's how the protocol handles DNS-related threats.

DNS Spoofing β†’ Redirect to Malicious IdP

Threat: An attacker controls the DNS response (cache poisoning, rogue resolver, local MITM) and points the user to a malicious IdP.

Why passkeys neutralize this: WebAuthn passkeys are origin-bound. The authenticator signs challenges only for the origin where the credential was originally registered. If an attacker redirects a user to evil-idp.example instead of the legitimate idp.example, the authenticator simply refuses to produce an assertion β€” the origins don't match. The attacker gets nothing.

This means DNS spoofing cannot be used to steal credentials or impersonate existing users.

Enrollment: The One Real Risk

During first-time enrollment, no passkey exists yet β€” there is no origin binding to protect the user. If DNS is spoofed at this exact moment, the user could register a passkey with a malicious IdP.

Mitigations:

  • Email verification: The IdP sends a verification email before completing enrollment. An attacker would need to control both DNS and the user's email.
  • Visual confirmation: The user sees the IdP URL in their browser. Unexpected domains are noticeable.
  • DNSSEC: Domain operators should enable DNSSEC to cryptographically sign DNS responses (see below).

Transport Security (DoH)

In edge and browser environments, DNS resolution typically uses DNS-over-HTTPS (DoH) via providers like Cloudflare (1.1.1.1), Google (8.8.8.8), or Quad9 (9.9.9.9). The TLS connection to the DoH provider protects against local network-level MITM attacks on DNS queries.

In Node.js and Bun server environments, DNS resolution uses the system resolver. Security depends on the deployment infrastructure β€” data center DNS is typically more trustworthy than coffee shop Wi-Fi, but operators should be aware of this difference.

DNSSEC cryptographically signs DNS responses, protecting against cache poisoning and response tampering. Domain operators using DDISA should enable DNSSEC as a best practice.

The protocol does not require DNSSEC because:

  1. Passkey origin binding already covers the critical authentication path (existing users cannot be phished via DNS spoofing)
  2. Enrollment is a one-time event with additional mitigations (email verification, visual confirmation)
  3. Forcing DNSSEC would exclude domains that haven't enabled it, reducing adoption without proportional security gain

Fallback Behavior

When no _ddisa. TXT record exists for a domain, the SP falls back to a configured default IdP. This is by design β€” not every domain will publish DDISA records.

The SP operator controls the fallback behavior: which default IdP to use, or whether to reject domains without DDISA records entirely. This is a deployment decision, not a protocol-level security boundary.

Minimal AuthN-JWT

The identity token contains only:

  • sub β€” email (consistent from input to token)
  • act β€” human or agent
  • iss, aud, exp, nonce

Explicitly not in the AuthN-JWT:

  • Name, display name β†’ IdP-internal
  • Owner, approver β†’ AuthZ layer (grants)

Rationale: AuthN says who. AuthZ says what may they do. Mixing them increases token size and leaks information the SP doesn't need.

Grant Security

Command Hash Binding

For escapes and similar tools, grants are bound to the exact command via SHA-256:

cmd_hash = sha256("apt-get upgrade")

The target system computes the hash locally and compares. If it doesn't match, execution is aborted. This prevents:

  • Substitution attacks β€” approving "whoami" but executing "rm -rf /"
  • Replay with different commands β€” the hash won't match

Dual Accountability

Grants track both the agent's identity and the approver's identity separately. In many setups, the agent owner and the approver are different people:

  • Agent owner: the person responsible for the agent
  • Approver: the person who approved this specific action

Both are recorded in the AuthZ-JWT (sub + decided_by).

Default Deny

No grant = no access. Agents start with zero permissions and must request each one. There is no "admin agent" concept β€” every privileged action requires explicit human approval (or a standing grant).

Management Token Separation

The IdP Management Token (NUXT_OPENAPE_MANAGEMENT_TOKEN) is an infrastructure credential β€” equivalent to a database root password or a cloud provider's admin key. It belongs exclusively to human administrators and must never be accessible to agents.

Why this boundary is critical:

If an agent obtains the Management Token, it can bypass the entire grant system:

  1. Self-registration β€” create new agent identities without human approval
  2. Grant self-approval β€” approve its own privilege requests
  3. Identity forgery β€” register arbitrary agents and issue tokens for them
  4. Audit destruction β€” the grant log becomes meaningless because the approver and requester are the same entity

This is not a theoretical risk β€” it is the single most important security boundary in an OpenApe deployment. The grant system's value depends entirely on the separation between who administers the IdP (humans with the Management Token) and who requests permissions through it (agents with Ed25519 keys).

Correct deployment:

  • Store the Management Token in a secrets manager or environment variable accessible only to the deployment infrastructure
  • Never write it into agent configuration files, shell histories, or automation scripts that agents can read
  • Use separate credentials for CI/CD pipelines β€” if a pipeline needs IdP access, give it a scoped API key, not the Management Token

Anti-patterns:

  • Passing the Management Token as a CLI argument to agent processes
  • Storing it in a shared .env file on a machine where agents run
  • Using the Management Token "temporarily" for agent enrollment (use the enrollment URL flow instead)

Credential Overview

CredentialWho holds itPurposeAgent access?
Management TokenHuman adminIdP administrationNEVER
Ed25519 Private KeyUser who operates the agentAgent authenticationYes (user-owned, generated during enrollment or provided)
PasskeyHuman userHuman authenticationHardware-bound
Session SecretDeploymentCookie signingInfrastructure only