diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-11-14 17:09:45 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-14 17:09:45 +0900 |
| commit | 65c5626b65ee00d2663ec3604140a18427b65cdc (patch) | |
| tree | 85ac0bdc22844fb0a21737fde4317de11afb85aa /packages/backend/src/server | |
| parent | update deps (diff) | |
| download | sharkey-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.ts | 58 | ||||
| -rw-r--r-- | packages/backend/src/server/ServerService.ts | 8 |
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, { |