Skip to content

Commit

Permalink
Fix: HTTP signature validation
Browse files Browse the repository at this point in the history
  • Loading branch information
atsu1125 committed Nov 27, 2023
1 parent c0333fd commit 4e4774a
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 3 deletions.
5 changes: 5 additions & 0 deletions packages/backend/src/queue/processors/inbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { LdSignature } from '@/remote/activitypub/misc/ld-signature.js';
import { StatusError } from '@/misc/fetch.js';
import { CacheableRemoteUser } from '@/models/entities/user.js';
import { UserPublickey } from '@/models/entities/user-publickey.js';
import { verifySignature } from '@/remote/activitypub/check-fetch.js';

const logger = new Logger('inbox');

Expand Down Expand Up @@ -80,6 +81,10 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
// HTTP-Signatureの検証
const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);

if (httpSignatureValidated) {
if (!verifySignature(signature, authUser.key)) return `skip: Invalid HTTP signature`;
}

// また、signatureのsignerは、activity.actorと一致する必要がある
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
Expand Down
23 changes: 23 additions & 0 deletions packages/backend/src/remote/activitypub/check-fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { IParsedSignature } from '@peertube/http-signature';
import { verify } from 'node:crypto';
import { toSingle } from '@/prelude/array.js';
import { createHash } from 'node:crypto';

export function verifySignature(sig: IParsedSignature, key: UserPublickey): boolean {
if (!['hs2019', 'rsa-sha256'].includes(sig.algorithm.toLowerCase())) return false;
try {
return verify('rsa-sha256', Buffer.from(sig.signingString, 'utf8'), key.keyPem, Buffer.from(sig.params.signature, 'base64'));
}
catch {
// Algo not supported
return false;
}
}

export function verifyDigest(body: string, digest: string | string[] | undefined): boolean {
digest = toSingle(digest);
if (body == null || digest == null || !digest.toLowerCase().startsWith('sha-256='))
return false;

return createHash('sha256').update(body).digest('base64') === digest.substring(8);
}
35 changes: 32 additions & 3 deletions packages/backend/src/server/activitypub.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Router from '@koa/router';
import json from 'koa-json-body';
import bodyParser from 'koa-bodyparser';
import httpSignature from '@peertube/http-signature';

import { renderActivity } from '@/remote/activitypub/renderer/index.js';
Expand All @@ -19,22 +20,35 @@ import { In, IsNull, Not } from 'typeorm';
import { renderLike } from '@/remote/activitypub/renderer/like.js';
import { getUserKeypair } from '@/misc/keypair-store.js';
import renderFollow from '@/remote/activitypub/renderer/follow.js';
import { verifyDigest } from '@/remote/activitypub/check-fetch.js';
import config from '@/config/index.js';
import Koa from 'koa';

// Init router
const router = new Router();

//#region Routing

function inbox(ctx: Router.RouterContext) {
if (ctx.req.headers.host !== config.host) {
ctx.status = 400;
return;
}

let signature;

try {
signature = httpSignature.parseRequest(ctx.req, { 'headers': [] });
signature = httpSignature.parseRequest(ctx.req, { headers: ['(request-target)', 'digest', 'host', 'date'] });
} catch (e) {
ctx.status = 401;
return;
}

if (!verifyDigest(ctx.request.rawBody, ctx.headers.digest)) {
ctx.status = 401;
return;
}

processInbox(ctx.request.body, signature);

ctx.status = 202;
Expand All @@ -58,9 +72,24 @@ export function setResponseType(ctx: Router.RouterContext) {
}
}

async function parseJsonBodyOrFail(ctx: Router.RouterContext, next: Koa.Next) {
const koaBodyParser = bodyParser({
enableTypes: ["json"],
detectJSON: () => true,
});

try {
await koaBodyParser(ctx, next);
}
catch {
ctx.status = 400;
return;
}
}

// inbox
router.post('/inbox', json(), inbox);
router.post('/users/:user/inbox', json(), inbox);
router.post('/inbox', parseJsonBodyOrFail, inbox);
router.post('/users/:user/inbox', parseJsonBodyOrFail, inbox);

// note
router.get('/notes/:note', async (ctx, next) => {
Expand Down

0 comments on commit 4e4774a

Please sign in to comment.