From c4ec2d09422e5f80d3a713b637b8704ce50ba342 Mon Sep 17 00:00:00 2001 From: Erin Shepherd Date: Tue, 17 Oct 2023 01:27:26 +0000 Subject: [PATCH] fix: Refetch user keys when HTTP Signature validation fails Co-authored-by: Erin Shepherd --- packages/backend/src/queue/processors/inbox.ts | 16 +++++++++++++++- .../src/remote/activitypub/check-fetch.ts | 16 +++++++++++++++- .../src/remote/activitypub/db-resolver.ts | 14 ++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index 0e500b89e..c8a092031 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -95,11 +95,25 @@ export default async (job: Bull.Job): Promise => { } // HTTP-Signatureの検証 - const httpSignatureValidated = httpSignature.verifySignature( + let httpSignatureValidated = httpSignature.verifySignature( signature, authUser.key.keyPem, ); + // If signature validation failed, try refetching the actor + if (!httpSignatureValidated) { + authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user); + + if (authUser.key == null) { + return "skip: failed to re-resolve user publicKey"; + } + + httpSignatureValidated = httpSignature.verifySignature( + signature, + authUser.key.keyPem, + ); + } + // また、signatureのsignerは、activity.actorと一致する必要がある if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { // 一致しなくても、でもLD-Signatureがありそうならそっちも見る diff --git a/packages/backend/src/remote/activitypub/check-fetch.ts b/packages/backend/src/remote/activitypub/check-fetch.ts index c885b4a19..b7fa179ac 100644 --- a/packages/backend/src/remote/activitypub/check-fetch.ts +++ b/packages/backend/src/remote/activitypub/check-fetch.ts @@ -86,11 +86,25 @@ export async function checkFetch(req: IncomingMessage): Promise { } // HTTP-Signatureの検証 - const httpSignatureValidated = httpSignature.verifySignature( + let httpSignatureValidated = httpSignature.verifySignature( signature, authUser.key.keyPem, ); + // If signature validation failed, try refetching the actor + if (!httpSignatureValidated) { + authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user); + + if (authUser.key == null) { + return 403; + } + + httpSignatureValidated = httpSignature.verifySignature( + signature, + authUser.key.keyPem, + ); + } + if (!httpSignatureValidated) { return 403; } diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/remote/activitypub/db-resolver.ts index a710b9f11..460f4a12d 100644 --- a/packages/backend/src/remote/activitypub/db-resolver.ts +++ b/packages/backend/src/remote/activitypub/db-resolver.ts @@ -17,7 +17,8 @@ import { Cache } from "@/misc/cache.js"; import { uriPersonCache, userByIdCache } from "@/services/user-cache.js"; import type { IObject } from "./type.js"; import { getApId } from "./type.js"; -import { resolvePerson } from "./models/person.js"; +import { resolvePerson, updatePerson } from "./models/person.js"; + const publicKeyCache = new Cache("publicKey", 60 * 30); const publicKeyByUserIdCache = new Cache( @@ -151,7 +152,7 @@ export default class DbResolver { */ public async getAuthUserFromKeyId(keyId: string): Promise<{ user: CacheableRemoteUser; - key: UserPublickey; + key: UserPublickey | null; } | null> { const key = await publicKeyCache.fetch( keyId, @@ -203,4 +204,13 @@ export default class DbResolver { key, }; } + + public async refetchPublicKeyForApId(user: CacheableRemoteUser): Promise { + await updatePerson(user.uri!, undefined, undefined, user); + let key = await UserPublickeys.findOneBy({ userId: user.id }); + if (key != null) { + await publicKeyByUserIdCache.set(user.id, key); + } + return key; + } }