sign-as-yields-impersonation
Signing tokens as an identity yields impersonation when a downstream trust validates those signatures.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?identity) in [
ServiceAccount, MachineIdentity, ManagedIdentity, WorkloadIdentity, ApplicationIdentity] # Azure SP is modeled as ApplicationIdentity
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'identity': None}
AuthenticatesTo {'relying_party': None}
emit
| source type | Identity |
|---|---|
| source | ?principal |
| target | ?identity |
| conditions | trust_relationship |
| state logic | if matched(optional[0]) (a relying party validates ?identity's tokens/assertions): ACTIVE; else CONDITIONAL(trust_exists) — the signing capability is real but no modeled trust consumes the signature yet, so no concrete authentication target is proven. Inherit weakest with the CanSignAs input; BLOCKED if CanSignAs is BLOCKED. |
Narrative
{principal.name} can sign tokens as {identity.name} ({perm:CanSignAs}); where a relying party validates {identity.name}'s signatures, {principal.name} can forge authentication and impersonate {identity.name}.
Raw rule rules/derived/credential-chains.yaml
id: sign-as-yields-impersonation
emits: CanImpersonate
description: Signing tokens as an identity yields impersonation when a downstream trust validates those
signatures.
applies_to:
- '*'
match:
- - principal: null
- CanSignAs
- signer: null
where:
- 'node_type(?identity) in [ServiceAccount, MachineIdentity, ManagedIdentity, WorkloadIdentity, ApplicationIdentity] #
Azure SP is modeled as ApplicationIdentity'
optional:
- - identity: null
- AuthenticatesTo
- relying_party: null
emit:
source_type: Identity
source: ?principal
target: ?identity
permissions: []
conditions:
- trust_relationship
state_logic: "if matched(optional[0]) (a relying party validates ?identity's tokens/assertions): ACTIVE;\
\ else CONDITIONAL(trust_exists) \u2014 the signing capability is real but no modeled trust consumes\
\ the signature yet, so no concrete authentication target is proven. Inherit weakest with the CanSignAs\
\ input; BLOCKED if CanSignAs is BLOCKED."
confidence: min(contributing_confidences) * 0.9
derived_from:
- ?principal CanSignAs ?signer
- ?signer -> ?identity (signs-for resolution)
- ?identity AuthenticatesTo ?relying_party?
false_positive_note: 'Conservative by design. Signing capability alone is not impersonation: a forged
JWT/SAML assertion authenticates only where some relying party trusts the signer''s key/issuer. Emit
ACTIVE only when such a trust is modeled (AuthenticatesTo / CanFederateAs / a validated OIDC issuer);
otherwise CONDITIONAL(trust_exists). Do not chain to escalation off a CONDITIONAL sign-as impersonation
until the consuming trust is confirmed. A SigningKey that no relying party trusts (e.g. an inactive/legacy
key) yields nothing.'
narrative: '{principal.name} can sign tokens as {identity.name} ({perm:CanSignAs}); where a relying
party validates {identity.name}''s signatures, {principal.name} can forge authentication and impersonate
{identity.name}.'