Changeset d0cab9d in Klonkt for src


Ignore:
Timestamp:
06/24/2026 03:59:47 PM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
f5c3870
Parents:
00f669b
Message:

feat(security): enforce HTTP signatures on the inbox

Data-affecting activities (Create/Like/Announce/Follow/Delete/Undo/Accept/Reject)
must now carry a valid HTTP signature whose signer matches the claimed actor —
else 401. Stops forged replies/likes/follows/timeline posts. Discovery (GET) open.

Co-Authored-By: Claude <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r00f669b rd0cab9d  
    358358  const type = act.type;
    359359  const base = (process.env.PUBLIC_BASE_URL || `${req.protocol}://${req.get('host')}`).replace(/\/+$/, '');
    360   const verified = await verifyRequest(req).catch(() => null); // best-effort; not gating (MVP)
     360  const verified = await verifyRequest(req).catch(() => null);
     361
     362  // ENFORCE HTTP signatures: a data-affecting activity must be signed by the very
     363  // actor it claims to be. No valid signature, or signer ≠ actor → reject (no
     364  // forged replies/likes/follows/timeline posts). GET/discovery stays open.
     365  const claimedActor = typeof act.actor === 'string' ? act.actor : (act.actor && act.actor.id);
     366  const GATED = ['Create', 'Like', 'Announce', 'Follow', 'Delete', 'Undo', 'Accept', 'Reject'];
     367  if (GATED.includes(type)) {
     368    if (!verified || !claimedActor || verified.id !== claimedActor) {
     369      console.warn('[AP] inbox REJECTED (signature)', type, claimedActor || '?', verified ? '(signer mismatch)' : '(unsigned/invalid)');
     370      return 401;
     371    }
     372  }
    361373
    362374  if (type === 'Follow') {
Note: See TracChangeset for help on using the changeset viewer.