From 6ae01e28aa717d54743f1ab44fd099853a969d3d Mon Sep 17 00:00:00 2001 From: dakkar Date: Tue, 30 Apr 2024 10:12:54 +0100 Subject: [PATCH 1/2] Compact LD-signed activities against well-known context This should defend against some spoofing attacks, see also https://nvd.nist.gov/vuln/detail/CVE-2022-24307 for Mastodon, https://iceshrimp.dev/iceshrimp/iceshrimp/commit/febb499fcb5fe3d56ca79025e4b5851464660c38 from Iceshrimp and https://firefish.dev/firefish/firefish/-/commit/e790d6be90dfd5dc6471b650a54520761bb9d745 for Firefish Thanks to @tesaguri@fedibird.com for reporting and providing the patch. --- .../src/core/activitypub/ApRendererService.ts | 44 +----------------- .../core/activitypub/LdSignatureService.ts | 10 ++++ .../src/core/activitypub/misc/contexts.ts | 46 ++++++++++++++++++- .../queue/processors/InboxProcessorService.ts | 14 +++++- 4 files changed, 70 insertions(+), 44 deletions(-) diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index a84feffac..d06bb2d8d 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -31,6 +31,7 @@ import { IdService } from '@/core/IdService.js'; import { MetaService } from '../MetaService.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; +import { CONTEXT } from './misc/contexts.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; @Injectable() @@ -785,48 +786,7 @@ export class ApRendererService { x.id = `${this.config.url}/${randomUUID()}`; } - return Object.assign({ - '@context': [ - 'https://www.w3.org/ns/activitystreams', - 'https://w3id.org/security/v1', - { - Key: 'sec:Key', - // as non-standards - manuallyApprovesFollowers: 'as:manuallyApprovesFollowers', - sensitive: 'as:sensitive', - Hashtag: 'as:Hashtag', - quoteUrl: 'as:quoteUrl', - fedibird: 'http://fedibird.com/ns#', - quoteUri: 'fedibird:quoteUri', - // 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_summary': 'misskey:_misskey_summary', - 'isCat': 'misskey:isCat', - // Firefish - firefish: 'https://joinfirefish.org/ns#', - speakAsCat: 'firefish:speakAsCat', - // Sharkey - sharkey: 'https://joinsharkey.org/ns#', - backgroundUrl: 'sharkey:backgroundUrl', - listenbrainz: 'sharkey:listenbrainz', - // vcard - vcard: 'http://www.w3.org/2006/vcard/ns#', - }, - ], - }, x as T & { id: string }); + return Object.assign({ '@context': CONTEXT }, x as T & { id: string }); } @bindThis diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts index 9de184336..a4add2255 100644 --- a/packages/backend/src/core/activitypub/LdSignatureService.ts +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -88,6 +88,16 @@ class LdSignature { return verifyData; } + @bindThis + public async compact(data: any, context: any = CONTEXT): Promise { + const customLoader = this.getLoader(); + // XXX: Importing jsonld dynamically since Jest frequently fails to import it statically + // https://github.com/misskey-dev/misskey/pull/9894#discussion_r1103753595 + return (await import('jsonld')).default.compact(data, context, { + documentLoader: customLoader, + }); + } + @bindThis public async normalize(data: JsonLdDocument): Promise { const customLoader = this.getLoader(); diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts index 88afdefcd..4ff114bbf 100644 --- a/packages/backend/src/core/activitypub/misc/contexts.ts +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import type { JsonLd } from 'jsonld/jsonld-spec.js'; +import type { Context, JsonLd } from 'jsonld/jsonld-spec.js'; /* eslint:disable:quotemark indent */ const id_v1 = { @@ -526,6 +526,50 @@ const activitystreams = { }, } satisfies JsonLd; +const context_iris = [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', +]; + +const extension_context_definition = { + Key: 'sec:Key', + // as non-standards + manuallyApprovesFollowers: 'as:manuallyApprovesFollowers', + sensitive: 'as:sensitive', + Hashtag: 'as:Hashtag', + quoteUrl: 'as:quoteUrl', + fedibird: 'http://fedibird.com/ns#', + quoteUri: 'fedibird:quoteUri', + // 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_summary': 'misskey:_misskey_summary', + 'isCat': 'misskey:isCat', + // Firefish + firefish: 'https://joinfirefish.org/ns#', + speakAsCat: 'firefish:speakAsCat', + // Sharkey + sharkey: 'https://joinsharkey.org/ns#', + backgroundUrl: 'sharkey:backgroundUrl', + listenbrainz: 'sharkey:listenbrainz', + // vcard + vcard: 'http://www.w3.org/2006/vcard/ns#', +} satisfies Context; + +export const CONTEXT: (string | Context)[] = [...context_iris, extension_context_definition]; + export const CONTEXTS: Record = { 'https://w3id.org/identity/v1': id_v1, 'https://w3id.org/security/v1': security_v1, diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index ad1d9799a..2b5b7c561 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -15,6 +15,7 @@ import InstanceChart from '@/core/chart/charts/instance.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import FederationChart from '@/core/chart/charts/federation.js'; import { getApId } from '@/core/activitypub/type.js'; +import type { IActivity } from '@/core/activitypub/type.js'; import type { MiRemoteUser } from '@/models/User.js'; import type { MiUserPublickey } from '@/models/UserPublickey.js'; import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; @@ -52,7 +53,7 @@ export class InboxProcessorService { @bindThis public async process(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); @@ -150,6 +151,17 @@ export class InboxProcessorService { throw new Bull.UnrecoverableError('skip: LD-Signatureの検証に失敗しました'); } + // アクティビティを正規化 + delete activity.signature; + try { + activity = await ldSignature.compact(activity) as IActivity; + } catch (e) { + throw new Bull.UnrecoverableError(`skip: failed to compact activity: ${e}`); + } + // TODO: 元のアクティビティと非互換な形に正規化される場合は転送をスキップする + // https://github.com/mastodon/mastodon/blob/664b0ca/app/services/activitypub/process_collection_service.rb#L24-L29 + activity.signature = ldSignature; + // もう一度actorチェック if (authUser.user.uri !== activity.actor) { throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`); From 45182c17e2e8a103a0cf39fde4c30fec9c5f6cc6 Mon Sep 17 00:00:00 2001 From: dakkar Date: Wed, 1 May 2024 17:41:33 +0100 Subject: [PATCH 2/2] fix imports --- packages/backend/src/core/activitypub/LdSignatureService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts index a4add2255..2ec6dc458 100644 --- a/packages/backend/src/core/activitypub/LdSignatureService.ts +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -7,7 +7,7 @@ import * as crypto from 'node:crypto'; import { Injectable } from '@nestjs/common'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; -import { CONTEXTS } from './misc/contexts.js'; +import { CONTEXT, CONTEXTS } from './misc/contexts.js'; import { validateContentTypeSetAsJsonLD } from './misc/validator.js'; import type { JsonLdDocument } from 'jsonld'; import type { JsonLd, RemoteDocument } from 'jsonld/jsonld-spec.js';