From febb499fcb5fe3d56ca79025e4b5851464660c38 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Thu, 18 Apr 2024 22:03:30 +0200 Subject: [PATCH] [backend] Compact LD-signed activities against well-known context to defend against spoofing attacks --- .../backend/src/queue/processors/inbox.ts | 4 +- .../src/remote/activitypub/misc/contexts.ts | 46 +++++++++++++++++++ .../remote/activitypub/misc/ld-signature.ts | 9 +++- .../src/remote/activitypub/renderer/index.ts | 42 +---------------- 4 files changed, 59 insertions(+), 42 deletions(-) diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index 6f3a98698..3c87727c4 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -29,7 +29,7 @@ const logger = new Logger("inbox"); // Processing when an activity arrives in the user's inbox export default async (job: Bull.Job): Promise => { const signature = job.data.signature; // HTTP-signature - const activity = job.data.activity; + let activity = job.data.activity; //#region Log const info = Object.assign({}, activity) as any; @@ -155,6 +155,8 @@ export default async (job: Bull.Job): Promise => { return "skip: LD-Signatureの検証に失敗しました"; } + activity = await ldSignature.compactToWellKnown(activity); + // もう一度actorチェック if (authUser.user.uri !== activity.actor) { return `skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`; diff --git a/packages/backend/src/remote/activitypub/misc/contexts.ts b/packages/backend/src/remote/activitypub/misc/contexts.ts index 8c97b5972..5d2009219 100644 --- a/packages/backend/src/remote/activitypub/misc/contexts.ts +++ b/packages/backend/src/remote/activitypub/misc/contexts.ts @@ -518,6 +518,52 @@ const activitystreams = { }, }; +export const WellKnownContext = { + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + // as non-standards + manuallyApprovesFollowers: "as:manuallyApprovesFollowers", + movedTo: { + "@id": "https://www.w3.org/ns/activitystreams#movedTo", + "@type": "@id" + }, + movedToUri: "as:movedTo", + sensitive: "as:sensitive", + Hashtag: "as:Hashtag", + quoteUri: "fedibird:quoteUri", + quoteUrl: "as:quoteUrl", + // Mastodon + toot: "http://joinmastodon.org/ns#", + Emoji: "toot:Emoji", + featured: "toot:featured", + discoverable: "toot:discoverable", + // schema + schema: "http://schema.org#", + PropertyValue: "schema:PropertyValue", + value: "schema:value", + // Misskey + misskey: "https://misskey-hub.net/ns#", + _misskey_content: "misskey:_misskey_content", + _misskey_quote: "misskey:_misskey_quote", + _misskey_reaction: "misskey:_misskey_reaction", + _misskey_votes: "misskey:_misskey_votes", + _misskey_talk: "misskey:_misskey_talk", + _misskey_summary: "misskey:_misskey_summary", + isCat: "misskey:isCat", + // Fedibird + fedibird: "http://fedibird.com/ns#", + // vcard + vcard: "http://www.w3.org/2006/vcard/ns#", + // litepub + litepub: "http://litepub.social/ns#", + EmojiReact: "litepub:EmojiReact", + EmojiReaction: "litepub:EmojiReaction", + }, + ], +}; + export const CONTEXTS: Record = { "https://w3id.org/identity/v1": id_v1, "https://w3id.org/security/v1": security_v1, diff --git a/packages/backend/src/remote/activitypub/misc/ld-signature.ts b/packages/backend/src/remote/activitypub/misc/ld-signature.ts index 62707624b..2d1e7ef6b 100644 --- a/packages/backend/src/remote/activitypub/misc/ld-signature.ts +++ b/packages/backend/src/remote/activitypub/misc/ld-signature.ts @@ -1,6 +1,6 @@ import * as crypto from "node:crypto"; import jsonld from "jsonld"; -import { CONTEXTS } from "./contexts.js"; +import { CONTEXTS, WellKnownContext } from "./contexts.js"; import fetch from "node-fetch"; import { httpAgent, httpsAgent } from "@/misc/fetch.js"; @@ -89,6 +89,13 @@ export class LdSignature { }); } + public async compactToWellKnown(data: any): Promise { + const options = { documentLoader: this.getLoader() }; + const context = WellKnownContext as any; + delete data["signature"]; + return await jsonld.compact(data, context, options); + } + private getLoader() { return async (url: string): Promise => { if (!url.match("^https?://")) throw new Error(`Invalid URL ${url}`); diff --git a/packages/backend/src/remote/activitypub/renderer/index.ts b/packages/backend/src/remote/activitypub/renderer/index.ts index a688b1194..7abbef957 100644 --- a/packages/backend/src/remote/activitypub/renderer/index.ts +++ b/packages/backend/src/remote/activitypub/renderer/index.ts @@ -4,6 +4,7 @@ import { getUserKeypair } from "@/misc/keypair-store.js"; import type { User } from "@/models/entities/user.js"; import { LdSignature } from "../misc/ld-signature.js"; import type { IActivity } from "../type.js"; +import { WellKnownContext } from "@/remote/activitypub/misc/contexts.js"; export const renderActivity = (x: any): IActivity | null => { if (x == null) return null; @@ -12,46 +13,7 @@ export const renderActivity = (x: any): IActivity | null => { x.id = `${config.url}/${uuid()}`; } - return Object.assign( - { - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - // as non-standards - manuallyApprovesFollowers: "as:manuallyApprovesFollowers", - movedToUri: "as:movedTo", - sensitive: "as:sensitive", - Hashtag: "as:Hashtag", - quoteUri: "fedibird:quoteUri", - quoteUrl: "as:quoteUrl", - // Mastodon - toot: "http://joinmastodon.org/ns#", - Emoji: "toot:Emoji", - featured: "toot:featured", - discoverable: "toot:discoverable", - // schema - schema: "http://schema.org#", - PropertyValue: "schema:PropertyValue", - value: "schema:value", - // Misskey - misskey: "https://misskey-hub.net/ns#", - _misskey_content: "misskey:_misskey_content", - _misskey_quote: "misskey:_misskey_quote", - _misskey_reaction: "misskey:_misskey_reaction", - _misskey_votes: "misskey:_misskey_votes", - _misskey_talk: "misskey:_misskey_talk", - _misskey_summary: "misskey:_misskey_summary", - isCat: "misskey:isCat", - // Fedibird - fedibird: "http://fedibird.com/ns#", - // vcard - vcard: "http://www.w3.org/2006/vcard/ns#", - }, - ], - }, - x, - ); + return Object.assign({}, WellKnownContext, x); }; export const attachLdSignature = async (