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>
This commit is contained in:
parent
30bb24d18c
commit
65c5626b65
4 changed files with 79 additions and 6 deletions
|
@ -59,7 +59,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.412.0",
|
"@aws-sdk/client-s3": "3.412.0",
|
||||||
"@aws-sdk/lib-storage": "3.412.0",
|
"@aws-sdk/lib-storage": "3.412.0",
|
||||||
"@smithy/node-http-handler": "2.1.5",
|
|
||||||
"@bull-board/api": "5.9.1",
|
"@bull-board/api": "5.9.1",
|
||||||
"@bull-board/fastify": "5.9.1",
|
"@bull-board/fastify": "5.9.1",
|
||||||
"@bull-board/ui": "5.9.1",
|
"@bull-board/ui": "5.9.1",
|
||||||
|
@ -78,6 +77,7 @@
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@simplewebauthn/server": "8.3.5",
|
"@simplewebauthn/server": "8.3.5",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
|
"@smithy/node-http-handler": "2.1.5",
|
||||||
"@swc/cli": "0.1.62",
|
"@swc/cli": "0.1.62",
|
||||||
"@swc/core": "1.3.96",
|
"@swc/core": "1.3.96",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
|
@ -99,6 +99,7 @@
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"fastify": "4.24.3",
|
"fastify": "4.24.3",
|
||||||
|
"fastify-raw-body": "^4.2.2",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "18.7.0",
|
"file-type": "18.7.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as crypto from 'node:crypto';
|
||||||
import { IncomingMessage } from 'node:http';
|
import { IncomingMessage } from 'node:http';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import fastifyAccepts from '@fastify/accepts';
|
import fastifyAccepts from '@fastify/accepts';
|
||||||
|
@ -108,7 +109,58 @@ export class ActivityPubServerService {
|
||||||
return;
|
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);
|
this.queueService.inbox(request.body as IActivity, signature);
|
||||||
|
|
||||||
reply.code(202);
|
reply.code(202);
|
||||||
|
@ -474,8 +526,8 @@ export class ActivityPubServerService {
|
||||||
|
|
||||||
//#region Routing
|
//#region Routing
|
||||||
// inbox (limit: 64kb)
|
// inbox (limit: 64kb)
|
||||||
fastify.post('/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', { 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
|
// note
|
||||||
fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
|
fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url';
|
||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import Fastify, { FastifyInstance } from 'fastify';
|
import Fastify, { FastifyInstance } from 'fastify';
|
||||||
import fastifyStatic from '@fastify/static';
|
import fastifyStatic from '@fastify/static';
|
||||||
|
import fastifyRawBody from 'fastify-raw-body';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { Config } from '@/config.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.
|
// 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`.
|
// `root` here is just a placeholder and each call must use its own `rootPath`.
|
||||||
fastify.register(fastifyStatic, {
|
fastify.register(fastifyStatic, {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
lockfileVersion: '6.0'
|
lockfileVersion: '6.1'
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
|
@ -182,6 +182,9 @@ importers:
|
||||||
fastify:
|
fastify:
|
||||||
specifier: 4.24.3
|
specifier: 4.24.3
|
||||||
version: 4.24.3
|
version: 4.24.3
|
||||||
|
fastify-raw-body:
|
||||||
|
specifier: ^4.2.2
|
||||||
|
version: 4.2.2
|
||||||
feed:
|
feed:
|
||||||
specifier: 4.2.2
|
specifier: 4.2.2
|
||||||
version: 4.2.2
|
version: 4.2.2
|
||||||
|
@ -7001,7 +7004,7 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@swc/core': ^1.2.66
|
'@swc/core': ^1.2.66
|
||||||
chokidar: 3.5.3
|
chokidar: ^3.5.1
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
chokidar:
|
chokidar:
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -11727,6 +11730,15 @@ packages:
|
||||||
resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==}
|
resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/fastify-raw-body@4.2.2:
|
||||||
|
resolution: {integrity: sha512-6l4fXtxNn7WOQiylu5fv9/JfUTvWCg1ED4gF44hqnVesgttOXEUMnNkdV8ZxwufCstRyUYaYSBIN4VuRHDbJkw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
dependencies:
|
||||||
|
fastify-plugin: 4.5.0
|
||||||
|
raw-body: 2.5.2
|
||||||
|
secure-json-parse: 2.7.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fastify@4.24.3:
|
/fastify@4.24.3:
|
||||||
resolution: {integrity: sha512-6HHJ+R2x2LS3y1PqxnwEIjOTZxFl+8h4kSC/TuDPXtA+v2JnV9yEtOsNSKK1RMD7sIR2y1ZsA4BEFaid/cK5pg==}
|
resolution: {integrity: sha512-6HHJ+R2x2LS3y1PqxnwEIjOTZxFl+8h4kSC/TuDPXtA+v2JnV9yEtOsNSKK1RMD7sIR2y1ZsA4BEFaid/cK5pg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in a new issue