summaryrefslogtreecommitdiff
path: root/packages/backend/src/queue/processors/InboxProcessorService.ts
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2024-07-20 21:33:20 +0900
committersyuilo <4439005+syuilo@users.noreply.github.com>2024-07-20 21:33:20 +0900
commit337b42bcb179bdfb993888ed94342a0158e8f3cb (patch)
treebd40424cf34c72b17effe19e5ce3cf866b3c6241 /packages/backend/src/queue/processors/InboxProcessorService.ts
parentdocs(misskey-js): fix broken i-want-you image link in README.md (#14265) (diff)
downloadsharkey-337b42bcb179bdfb993888ed94342a0158e8f3cb.tar.gz
sharkey-337b42bcb179bdfb993888ed94342a0158e8f3cb.tar.bz2
sharkey-337b42bcb179bdfb993888ed94342a0158e8f3cb.zip
revert 5f88d56d96
バグがある(かつすぐに修正できそうにない) & まだレビュー途中で意図せずマージされたため
Diffstat (limited to 'packages/backend/src/queue/processors/InboxProcessorService.ts')
-rw-r--r--packages/backend/src/queue/processors/InboxProcessorService.ts130
1 files changed, 56 insertions, 74 deletions
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 935c623df1..fa7009f8f5 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -5,8 +5,8 @@
import { URL } from 'node:url';
import { Injectable } from '@nestjs/common';
+import httpSignature from '@peertube/http-signature';
import * as Bull from 'bullmq';
-import { verifyDraftSignature } from '@misskey-dev/node-http-message-signatures';
import type Logger from '@/logger.js';
import { MetaService } from '@/core/MetaService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
@@ -20,7 +20,6 @@ import type { MiRemoteUser } from '@/models/User.js';
import type { MiUserPublickey } from '@/models/UserPublickey.js';
import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
import { StatusError } from '@/misc/status-error.js';
-import * as Acct from '@/misc/acct.js';
import { UtilityService } from '@/core/UtilityService.js';
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import { JsonLdService } from '@/core/activitypub/JsonLdService.js';
@@ -53,15 +52,8 @@ export class InboxProcessorService {
@bindThis
public async process(job: Bull.Job<InboxJobData>): Promise<string> {
- const signature = job.data.signature ?
- 'version' in job.data.signature ? job.data.signature.value : job.data.signature
- : null;
- if (Array.isArray(signature)) {
- // RFC 9401はsignatureが配列になるが、とりあえずエラーにする
- throw new Error('signature is array');
- }
+ const signature = job.data.signature; // HTTP-signature
let activity = job.data.activity;
- let actorUri = getApId(activity.actor);
//#region Log
const info = Object.assign({}, activity);
@@ -69,7 +61,7 @@ export class InboxProcessorService {
this.logger.debug(JSON.stringify(info, null, 2));
//#endregion
- const host = this.utilityService.toPuny(new URL(actorUri).hostname);
+ const host = this.utilityService.toPuny(new URL(signature.keyId).hostname);
// ブロックしてたら中断
const meta = await this.metaService.fetch();
@@ -77,76 +69,69 @@ export class InboxProcessorService {
return `Blocked request: ${host}`;
}
+ const keyIdLower = signature.keyId.toLowerCase();
+ if (keyIdLower.startsWith('acct:')) {
+ return `Old keyId is no longer supported. ${keyIdLower}`;
+ }
+
// HTTP-Signature keyIdを元にDBから取得
- let authUser: Awaited<ReturnType<typeof this.apDbResolverService.getAuthUserFromApId>> = null;
- let httpSignatureIsValid = null as boolean | null;
+ let authUser: {
+ user: MiRemoteUser;
+ key: MiUserPublickey | null;
+ } | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
- try {
- authUser = await this.apDbResolverService.getAuthUserFromApId(actorUri, signature?.keyId);
- } catch (err) {
- // 対象が4xxならスキップ
- if (err instanceof StatusError) {
- if (!err.isRetryable) {
- throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
+ // keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得
+ if (authUser == null) {
+ try {
+ authUser = await this.apDbResolverService.getAuthUserFromApId(getApId(activity.actor));
+ } catch (err) {
+ // 対象が4xxならスキップ
+ if (err instanceof StatusError) {
+ if (!err.isRetryable) {
+ throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
+ }
+ throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
}
- throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
}
}
- // authUser.userがnullならスキップ
- if (authUser != null && authUser.user == null) {
+ // それでもわからなければ終了
+ if (authUser == null) {
throw new Bull.UnrecoverableError('skip: failed to resolve user');
}
- if (signature != null && authUser != null) {
- if (signature.keyId.toLowerCase().startsWith('acct:')) {
- this.logger.warn(`Old keyId is no longer supported. lowerKeyId=${signature.keyId.toLowerCase()}`);
- } else if (authUser.key != null) {
- // keyがなかったらLD Signatureで検証するべき
- // HTTP-Signatureの検証
- const errorLogger = (ms: any) => this.logger.error(ms);
- httpSignatureIsValid = await verifyDraftSignature(signature, authUser.key.keyPem, errorLogger);
- this.logger.debug('Inbox message validation: ', {
- userId: authUser.user.id,
- userAcct: Acct.toString(authUser.user),
- parsedKeyId: signature.keyId,
- foundKeyId: authUser.key.keyId,
- httpSignatureValid: httpSignatureIsValid,
- });
- }
+ // publicKey がなくても終了
+ if (authUser.key == null) {
+ throw new Bull.UnrecoverableError('skip: failed to resolve user publicKey');
}
- if (
- authUser == null ||
- httpSignatureIsValid !== true ||
- authUser.user.uri !== actorUri // 一応チェック
- ) {
+ // HTTP-Signatureの検証
+ const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
+
+ // また、signatureのsignerは、activity.actorと一致する必要がある
+ if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
const ldSignature = activity.signature;
-
- if (ldSignature && ldSignature.creator) {
+ if (ldSignature) {
if (ldSignature.type !== 'RsaSignature2017') {
throw new Bull.UnrecoverableError(`skip: unsupported LD-signature type ${ldSignature.type}`);
}
- if (ldSignature.creator.toLowerCase().startsWith('acct:')) {
- throw new Bull.UnrecoverableError(`old key not supported ${ldSignature.creator}`);
+ // ldSignature.creator: https://example.oom/users/user#main-key
+ // みたいになっててUserを引っ張れば公開キーも入ることを期待する
+ if (ldSignature.creator) {
+ const candicate = ldSignature.creator.replace(/#.*/, '');
+ await this.apPersonService.resolvePerson(candicate).catch(() => null);
}
- authUser = await this.apDbResolverService.getAuthUserFromApId(actorUri, ldSignature.creator);
-
+ // keyIdからLD-Signatureのユーザーを取得
+ authUser = await this.apDbResolverService.getAuthUserFromKeyId(ldSignature.creator);
if (authUser == null) {
- throw new Bull.UnrecoverableError(`skip: LD-Signatureのactorとcreatorが一致しませんでした uri=${actorUri} creator=${ldSignature.creator}`);
- }
- if (authUser.user == null) {
- throw new Bull.UnrecoverableError(`skip: LD-Signatureのユーザーが取得できませんでした uri=${actorUri} creator=${ldSignature.creator}`);
- }
- // 一応actorチェック
- if (authUser.user.uri !== actorUri) {
- throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${actorUri})`);
+ throw new Bull.UnrecoverableError('skip: LD-Signatureのユーザーが取得できませんでした');
}
+
if (authUser.key == null) {
- throw new Bull.UnrecoverableError(`skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした uri=${actorUri} creator=${ldSignature.creator}`);
+ throw new Bull.UnrecoverableError('skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした');
}
const jsonLd = this.jsonLdService.use();
@@ -157,27 +142,13 @@ export class InboxProcessorService {
throw new Bull.UnrecoverableError('skip: LD-Signatureの検証に失敗しました');
}
- // ブロックしてたら中断
- const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
- if (this.utilityService.isBlockedHost(meta.blockedHosts, ldHost)) {
- throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`);
- }
-
// アクティビティを正規化
- // GHSA-2vxv-pv3m-3wvj
delete activity.signature;
try {
activity = await jsonLd.compact(activity) as IActivity;
} catch (e) {
throw new Bull.UnrecoverableError(`skip: failed to compact activity: ${e}`);
}
-
- // actorが正規化前後で一致しているか確認
- actorUri = getApId(activity.actor);
- if (authUser.user.uri !== actorUri) {
- throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity(after normalization).actor(${actorUri})`);
- }
-
// TODO: 元のアクティビティと非互換な形に正規化される場合は転送をスキップする
// https://github.com/mastodon/mastodon/blob/664b0ca/app/services/activitypub/process_collection_service.rb#L24-L29
activity.signature = ldSignature;
@@ -187,8 +158,19 @@ export class InboxProcessorService {
delete compactedInfo['@context'];
this.logger.debug(`compacted: ${JSON.stringify(compactedInfo, null, 2)}`);
//#endregion
+
+ // もう一度actorチェック
+ if (authUser.user.uri !== activity.actor) {
+ throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`);
+ }
+
+ // ブロックしてたら中断
+ const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
+ if (this.utilityService.isBlockedHost(meta.blockedHosts, ldHost)) {
+ throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`);
+ }
} else {
- throw new Bull.UnrecoverableError(`skip: http-signature verification failed and no LD-Signature. http_signature_keyId=${signature?.keyId}`);
+ throw new Bull.UnrecoverableError(`skip: http-signature verification failed and no LD-Signature. keyId=${signature.keyId}`);
}
}