Bind HTTP-signature signer to activity.actor on all AP inbox activity types #82
Labels
No labels
bug
duplicate
enhancement
future
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/TeaLeaves#82
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: Medium · Confidence: High · Effort: S · Category: security
Evidence
api/src/routes/activitypub.ts:402-446(Follow),502-519(Like/Announce),522-621(Create-comment; storesap_actor_urland resolvesauthor_namefromactivity.actorat 604-618).api/src/routes/activitypub.ts:312-326- the entry-write path DOES bind signer?actor viaparseSignatureKeyId(the correct pattern, applied only there).Problem
verifyInboxSignatureproves the request was signed by the owner ofkeyId, but for every activity type except the entry-write path it does not assertkeyIdowner ==activity.actor. An attacker controlling any domain (valid actor doc + key) can sign a request while settingactivity.actorto an arbitrary third party.Impact
Impersonation of arbitrary fediverse identities in public motif comments (stored with a spoofed
ap_actor_urland the victim's real display name), plus forced Follow rows and an unsolicited signedAccept. Content is sanitized, so no XSS - identity spoofing, not code execution.Fix
After
verifyInboxSignature, reject whenparseSignatureKeyId(signature) !== activity.actorfor all activity types (hoist the check the entry-write path already does).Acceptance criteria
Related: SSRF hardening (F-02) on the same inbox.
Filed from the 2026-07-15 codebase audit. Full report:
docs/.internal/report-2026-07-15.md(gitignored).Fixed in
ab0e89e(+ test fixf4ea594) — v7.1.0 wave 2.Hoisted the signer↔actor binding out of
tryRemoteEntryWriteso it runs for every activity type, immediately afterverifyInboxSignatureand before any handler readsactivity.actor:Covers Follow / Invite / Undo / Like / Announce / Create / Delete. The entry-write path keeps its own check as defence in depth.
Acceptance criteria:
api/src/test/integration/apSignature.test.tsasserts a Like signed byalicebut claimingactor: mallory→ 401, with a matching-actor control returning 202 (so the 401 is provably the binding, not a bad signature). Verified on the dev server:[AP inbox] signature actor mismatch: { verifiedSigner: '…/alice', claimedActor: '…/mallory' }CI green (typecheck + 239 tests).