[backend] Compact LD-signed activities against well-known context to defend against spoofing attacks

This commit is contained in:
Laura Hausmann 2024-04-18 22:03:30 +02:00
parent dcfa69ff9d
commit febb499fcb
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 59 additions and 42 deletions

View file

@ -29,7 +29,7 @@ const logger = new Logger("inbox");
// Processing when an activity arrives in the user's inbox // Processing when an activity arrives in the user's inbox
export default async (job: Bull.Job<InboxJobData>): Promise<string> => { export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
const signature = job.data.signature; // HTTP-signature const signature = job.data.signature; // HTTP-signature
const activity = job.data.activity; let activity = job.data.activity;
//#region Log //#region Log
const info = Object.assign({}, activity) as any; const info = Object.assign({}, activity) as any;
@ -155,6 +155,8 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
return "skip: LD-Signatureの検証に失敗しました"; return "skip: LD-Signatureの検証に失敗しました";
} }
activity = await ldSignature.compactToWellKnown(activity);
// もう一度actorチェック // もう一度actorチェック
if (authUser.user.uri !== activity.actor) { if (authUser.user.uri !== activity.actor) {
return `skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`; return `skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`;

View file

@ -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<string, unknown> = { export const CONTEXTS: Record<string, unknown> = {
"https://w3id.org/identity/v1": id_v1, "https://w3id.org/identity/v1": id_v1,
"https://w3id.org/security/v1": security_v1, "https://w3id.org/security/v1": security_v1,

View file

@ -1,6 +1,6 @@
import * as crypto from "node:crypto"; import * as crypto from "node:crypto";
import jsonld from "jsonld"; import jsonld from "jsonld";
import { CONTEXTS } from "./contexts.js"; import { CONTEXTS, WellKnownContext } from "./contexts.js";
import fetch from "node-fetch"; import fetch from "node-fetch";
import { httpAgent, httpsAgent } from "@/misc/fetch.js"; import { httpAgent, httpsAgent } from "@/misc/fetch.js";
@ -89,6 +89,13 @@ export class LdSignature {
}); });
} }
public async compactToWellKnown(data: any): Promise<any> {
const options = { documentLoader: this.getLoader() };
const context = WellKnownContext as any;
delete data["signature"];
return await jsonld.compact(data, context, options);
}
private getLoader() { private getLoader() {
return async (url: string): Promise<any> => { return async (url: string): Promise<any> => {
if (!url.match("^https?://")) throw new Error(`Invalid URL ${url}`); if (!url.match("^https?://")) throw new Error(`Invalid URL ${url}`);

View file

@ -4,6 +4,7 @@ import { getUserKeypair } from "@/misc/keypair-store.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { LdSignature } from "../misc/ld-signature.js"; import { LdSignature } from "../misc/ld-signature.js";
import type { IActivity } from "../type.js"; import type { IActivity } from "../type.js";
import { WellKnownContext } from "@/remote/activitypub/misc/contexts.js";
export const renderActivity = (x: any): IActivity | null => { export const renderActivity = (x: any): IActivity | null => {
if (x == null) return null; if (x == null) return null;
@ -12,46 +13,7 @@ export const renderActivity = (x: any): IActivity | null => {
x.id = `${config.url}/${uuid()}`; x.id = `${config.url}/${uuid()}`;
} }
return Object.assign( return Object.assign({}, WellKnownContext, x);
{
"@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,
);
}; };
export const attachLdSignature = async ( export const attachLdSignature = async (