summaryrefslogtreecommitdiff
path: root/packages/backend/src/server
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-11-14 17:09:45 +0900
committerGitHub <noreply@github.com>2023-11-14 17:09:45 +0900
commit65c5626b65ee00d2663ec3604140a18427b65cdc (patch)
tree85ac0bdc22844fb0a21737fde4317de11afb85aa /packages/backend/src/server
parentupdate deps (diff)
downloadsharkey-65c5626b65ee00d2663ec3604140a18427b65cdc.tar.gz
sharkey-65c5626b65ee00d2663ec3604140a18427b65cdc.tar.bz2
sharkey-65c5626b65ee00d2663ec3604140a18427b65cdc.zip
Merge pull request from GHSA-3f39-6537-3cgc
This commit implements HTTP header and body validation to fix [SIF-2023-002](https://advisory.silicon.moe/advisory/sif-2023-002/) Signed-off-by: perillamint <perillamint@silicon.moe> Co-authored-by: perillamint <perillamint@silicon.moe> Co-authored-by: yunochi <yuno@yunochi.com>
Diffstat (limited to 'packages/backend/src/server')
-rw-r--r--packages/backend/src/server/ActivityPubServerService.ts58
-rw-r--r--packages/backend/src/server/ServerService.ts8
2 files changed, 63 insertions, 3 deletions
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 2e64d41c91..17c503ef8e 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import * as crypto from 'node:crypto';
import { IncomingMessage } from 'node:http';
import { Inject, Injectable } from '@nestjs/common';
import fastifyAccepts from '@fastify/accepts';
@@ -108,7 +109,58 @@ export class ActivityPubServerService {
return;
}
- // TODO: request.bodyのバリデーション?
+ if (signature.params.headers.indexOf('host') === -1
+ || request.headers.host !== this.config.host) {
+ // Host not specified or not match.
+ reply.code(401);
+ return;
+ }
+
+ if (signature.params.headers.indexOf('digest') === -1) {
+ // Digest not found.
+ reply.code(401);
+ } else {
+ const digest = request.headers.digest;
+
+ if (typeof digest !== 'string') {
+ // Huh?
+ reply.code(401);
+ return;
+ }
+
+ const re = /^([a-zA-Z0-9\-]+)=(.+)$/;
+ const match = digest.match(re);
+
+ if (match == null) {
+ // Invalid digest
+ reply.code(401);
+ return;
+ }
+
+ const algo = match[1];
+ const digestValue = match[2];
+
+ if (algo !== 'SHA-256') {
+ // Unsupported digest algorithm
+ reply.code(401);
+ return;
+ }
+
+ if (request.rawBody == null) {
+ // Bad request
+ reply.code(400);
+ return;
+ }
+
+ const hash = crypto.createHash('sha256').update(request.rawBody).digest('base64');
+
+ if (hash !== digestValue) {
+ // Invalid digest
+ reply.code(401);
+ return;
+ }
+ }
+
this.queueService.inbox(request.body as IActivity, signature);
reply.code(202);
@@ -474,8 +526,8 @@ export class ActivityPubServerService {
//#region Routing
// inbox (limit: 64kb)
- fastify.post('/inbox', { bodyLimit: 1024 * 64 }, async (request, reply) => await this.inbox(request, reply));
- fastify.post('/users/:user/inbox', { bodyLimit: 1024 * 64 }, async (request, reply) => await this.inbox(request, reply));
+ fastify.post('/inbox', { config: { rawBody: true }, bodyLimit: 1024 * 64 }, async (request, reply) => await this.inbox(request, reply));
+ fastify.post('/users/:user/inbox', { config: { rawBody: true }, bodyLimit: 1024 * 64 }, async (request, reply) => await this.inbox(request, reply));
// note
fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 757cf21615..6e1956cd1d 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import Fastify, { FastifyInstance } from 'fastify';
import fastifyStatic from '@fastify/static';
+import fastifyRawBody from 'fastify-raw-body';
import { IsNull } from 'typeorm';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { Config } from '@/config.js';
@@ -86,6 +87,13 @@ export class ServerService implements OnApplicationShutdown {
});
}
+ // Register raw-body parser for ActivityPub HTTP signature validation.
+ fastify.register(fastifyRawBody, {
+ global: false,
+ encoding: 'utf-8',
+ runFirst: true,
+ });
+
// Register non-serving static server so that the child services can use reply.sendFile.
// `root` here is just a placeholder and each call must use its own `rootPath`.
fastify.register(fastifyStatic, {