aic-agent — Threat model (EN)EN
aic-agent threat model
What an attacker can and cannot break, the controls the SDK enforces, and what it deliberately leaves to the deployment. Complements SECURITY.md (policy and reporting); architecture.md describes the normal operation.
In scope. Everything in package aicagent when used through its documented
surfaces (Agent, Config, bearer, identity, transport) with correctly-
configured trust material.
Assumed attacker. Remote, unauthenticated by definition: anyone who can reach the endpoints the agent calls, or who can intercept the wire between agent and service. She can read all public data, observe the wire, and present any challenge at the service boundary. She does not hold the agent key, the principal key, the user-signer, or the configuration.
Assets
| Asset | Why it matters | Where it lives |
|---|---|---|
| Agent private key | all minted credibility flows from it | in-memory; the code that calls identity.GenerateKey or Load |
| Bearer token | authenticates requests to the protected service | agent state, HTTP headers (mutable per request) |
| Delegation authorization (DA) | binds the principal's authority to this agent | the token the agent presents; the signer's HTTP response |
| The protected service | the value the attacker is trying to reach | out of scope for this SDK's controls (the agent is the client) |
| Evidence that the service required | to close an evidence-required denial | held by the deployment, never the agent |
Trusted components
- The agent key (
Config.Key). Whoever holds this key can mint identities the verifier will authenticate (subject to capabilities). Compromise of this key = full impersonation of the agent. - The user signer (
Config.UserSigner). When the agent has no AIC yet, it asks the signer for a DA. The signer holds the principal's identity key; whoever controls the signer controls what agents are authorized. - The remote issuer (
Config.RemoteIssuer). Exchanges a DA for a short-lived token; a compromised issuer can mint bearer credentials directly. - The protected service's CA (
ServerCA). Pins the TLS identity of the backend; a compromised service key + compromised CA allows credential theft on the wire for Bearer mode.
Threats and controls
T1 — Key theft / extraction from memory
Attacker extracts the agent key from the host, a coredump, or a leaked back-up file.
- Mitigation: the key is in-memory for the process lifetime; protect the
file on disk (
0600, private directory). Prefer KMS/HSM-backed signers when the deployment has one — theKeyis acrypto.Signervalue, not a path, for exactly this reason. - Residual: a runtime memory read (rooted host) defeats the key. Accept this as out of scope; use hardened runtimes (sandboxing, eBPF) at the platform layer.
T2 — Token theft on the wire / leaked log
Attacker reads the bearer token from a log, from the wire, or from a cached response body.
ReplayProtection(single-usejti) limits the window to one request per minted token.- Local Bearer mode mints a fresh token per request; the transport never
reuses the same
jti. - Use HTTPS end-to-end and short
explifetimes. The SDK does not set them — the issuer does. - Residual: a stolen token is valid until first use and
exp; the deployment's WAF/edge layer should rate-limit concurrent requests per identity.
T3 — CA/key compromise (principal or issuer)
Attacker holds the CA key that signed the agent's credential, or the issuer's signing key.
- Out of scope for the client SDK. Mitigation is operational: rotate/revoke the CA, shorten token lifetimes, refresh the trust anchor.
ServerCApinning protects the service identity from a compromised system CA; it does not help with issuer compromise.
T4 — Replay / downgrade of a client challenge retry
Attacker replays a CLC-CHALLENGE-v1 challenge that already expired, or tries to force a second retry.
- The SDK retries at most once per challenge, and only when
Challenge.Usable(live, not expired) at the exact instant of the retry. An expired or stale challenge is never retried — that is the load amplification the challenge spec forbids. - The request retried is byte-identical (same marshalled body, via
GetBody); there is no mutation path the attacker can inject. ChallengeUnavailableis surfaced, not silently swallowed.
T5 — Bearer token reuse across identities / across verifiers
Attacker captures a valid token and presents it to a different verifier, or
a different agent, hoping cnf.jkt doesn't catch the mismatch.
cnf.jkt(RFC 7638 presenter thumbprint) binds the token to this agent's key; a verifier checkingcnfrejects a token presented with a different key.kid(SPKI hash) identifies the issuing key — not the service's key, so cross-verifier token theft doesn't help unless the verifier also trusts the same issuer CA (operational trust boundary).
T6 — Refresh-on-401 body safety
Attacker modifies a POST body, triggers a 401, and the client retries with a different (attacker-chosen) body.
- The SDK only retries a 401 when the request is idempotent (GET/HEAD/OPTIONS/
TRACE), the body is
nil, or the request carries aGetBodyfunction so the original body can be recreated for the retry. A non-idempotent request withoutGetBodyreturns the 401 directly.
T7 — DoS / resource exhaustion
Flood of evidence-required challenges that the agent keeps retrying, or a rate of token requests that overwhelms the signer.
- Rate limiting is the edge's and the issuer's job; the SDK does not rehammer the verifier beyond the one-retry limit.
- Evidence-retry waits are bounded (
Retry-After/retry_timing.not_before, never pastexpires_at) and require the deployment's provider to returntrue— there is no path where the SDK hammers the verifier on its own.
Escalation
- Agent key + server CA compromise: attacker can mint valid tokens for this agent and complete the TLS handshake with the verifier's service. Mitigation: rotate the agent key, revoke the CA, rotate the verifier's service TLS cert. The client SDK cannot help with its own trust root lying.
- Issuer + signer compromise: attacker can sign any delegation for any principal. Mitigation: operational trust boundary (different keys, separate HSMs) — this is outside the SDK's scope.
Controls matrix (quick reference)
| Control | Threat |
|---|---|
per-request jti / ReplayProtection |
T1, T2 |
cnf.jkt presenter binding |
T2, T5 |
kid SPKI |
T2, T5 |
Challenge.Usable + single-retry |
T4, T7 |
GetBody / idempotency check |
T6 |
ServerCA pinning |
T3 |
AllowPlaintextHTTP = false (default) |
MITM on token endpoint |
RootCA != ServerCA separation |
SDK-managed outbound vs service TLS |
Out of scope
- Agent key storage security (platform/HSM).
- Bearer token lifetime and minting policy (the issuer's decision).
- Rate limiting at the edge (edge responsibility).
- Defense against a compromised trust root (see Escalation).
References
- SECURITY.md — reporting + guarantees
- deployment.md — hardening in practice
- architecture.md — credential lifecycle, retry machine
- reference.md — Config, refusal shape, constants