diff --git a/package.json b/package.json index b76e40d278..1f59765fa5 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "cbor": "9.0.1", "chalk": "4.1.2", "cli-highlight": "2.1.11", + "co-body": "6.1.0", "content-disposition": "0.5.4", "crc-32": "1.2.2", "cross-env": "7.0.3", @@ -99,7 +100,6 @@ "koa": "2.14.2", "koa-bodyparser": "4.4.1", "koa-favicon": "2.1.0", - "koa-json-body": "5.3.0", "koa-logger": "3.2.1", "koa-mount": "4.0.0", "koa-send": "5.0.1", @@ -201,6 +201,7 @@ "@types/bcryptjs": "2.4.6", "@types/bull": "4.10.0", "@types/cbor": "6.0.0", + "@types/co-body": "6.1.3", "@types/dateformat": "3.0.1", "@types/double-ended-queue": "2.1.7", "@types/escape-regexp": "0.0.3", diff --git a/src/@types/koa-json-body.d.ts b/src/@types/koa-json-body.d.ts deleted file mode 100644 index 7e9516a0c3..0000000000 --- a/src/@types/koa-json-body.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare module 'koa-json-body' { - import { Middleware } from 'koa'; - - interface IKoaJsonBodyOptions { - strict?: boolean; - limit?: string; - fallback?: boolean; - } - - function koaJsonBody(opt?: IKoaJsonBodyOptions): Middleware; - - namespace koaJsonBody {} // Hack - - export = koaJsonBody; -} diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts index aab027e2c9..578430d1d4 100644 --- a/src/server/activitypub.ts +++ b/src/server/activitypub.ts @@ -1,6 +1,11 @@ import * as Router from '@koa/router'; -import * as json from 'koa-json-body'; +import config from '../config'; +import * as coBody from 'co-body'; +import * as crypto from 'crypto'; +import { IActivity } from '../remote/activitypub/type'; import * as httpSignature from 'http-signature'; +import Logger from '../services/logger'; +import { inspect } from 'util'; import { renderActivity } from '../remote/activitypub/renderer'; import renderNote from '../remote/activitypub/renderer/note'; @@ -18,22 +23,72 @@ import { ILocalUser, User } from '../models/entities/user'; import { In } from 'typeorm'; import { ensure } from '../prelude/ensure'; +const logger = new Logger('activitypub'); + // Init router const router = new Router(); //#region Routing -function inbox(ctx: Router.RouterContext) { - let signature; +async function inbox(ctx: Router.RouterContext) { + if (ctx.req.headers.host !== config.host) { + ctx.status = 400; + return; + } + + // parse body + const { parsed, raw } = await coBody.json(ctx, { + limit: '64kb', + returnRawBody: true, + }); + ctx.request.body = parsed; + + let signature: httpSignature.IParsedSignature; try { - signature = httpSignature.parseRequest(ctx.req, { 'headers': [] }); + signature = httpSignature.parseRequest(ctx.req, { 'headers': ['(request-target)', 'digest', 'host', 'date'] }); } catch (e) { + logger.warn(`inbox: signature parse error: ${inspect(e)}`); + ctx.status = 401; + return; + } + + // Digestヘッダーの検証 + const digest = ctx.req.headers.digest; + + // 無いとか複数あるとかダメ! + if (typeof digest !== 'string') { + logger.warn(`inbox: unrecognized digest header 1`); + ctx.status = 401; + return; + } + + const match = digest.match(/^([0-9A-Za-z-]+)=(.+)$/); + + if (match == null) { + logger.warn(`inbox: unrecognized digest header 2`); + ctx.status = 401; + return; + } + + const digestAlgo = match[1]; + const digestExpected = match[2]; + + if (digestAlgo.toUpperCase() !== 'SHA-256') { + logger.warn(`inbox: unsupported algorithm`); + ctx.status = 401; + return; + } + + const digestActual = crypto.createHash('sha256').update(raw).digest('base64'); + + if (digestExpected !== digestActual) { + logger.warn(`inbox: digest missmatch`); ctx.status = 401; return; } - processInbox(ctx.request.body, signature); + processInbox(ctx.request.body as IActivity, signature); ctx.status = 202; } @@ -59,8 +114,8 @@ export function setResponseType(ctx: Router.RouterContext) { } // inbox -router.post('/inbox', json({ limit: '64kb' }), inbox); -router.post('/users/:user/inbox', json({ limit: '64kb' }), inbox); +router.post('/inbox', inbox); +router.post('/users/:user/inbox', inbox); // note router.get('/notes/:note', async (ctx, next) => { diff --git a/yarn.lock b/yarn.lock index dd371ff518..31706434b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -831,6 +831,14 @@ dependencies: cbor "*" +"@types/co-body@6.1.3": + version "6.1.3" + resolved "https://registry.yarnpkg.com/@types/co-body/-/co-body-6.1.3.tgz#201796c6389066b400cfcb4e1ec5c3db798265a2" + integrity sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -2692,15 +2700,15 @@ cluster-key-slot@^1.1.0: resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== -co-body@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/co-body/-/co-body-5.2.0.tgz#5a0a658c46029131e0e3a306f67647302f71c124" - integrity sha512-sX/LQ7LqUhgyaxzbe7IqwPeTr2yfpfUIQ/dgpKo6ZI4y4lpQA0YxAomWIY+7I7rHWcG02PG+OuPREzMW/5tszQ== +co-body@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/co-body/-/co-body-6.1.0.tgz#d87a8efc3564f9bfe3aced8ef5cd04c7a8766547" + integrity sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ== dependencies: inflation "^2.0.0" - qs "^6.4.0" - raw-body "^2.2.0" - type-is "^1.6.14" + qs "^6.5.2" + raw-body "^2.3.3" + type-is "^1.6.16" co-body@^6.0.0: version "6.0.0" @@ -5904,13 +5912,6 @@ koa-favicon@2.1.0: dependencies: mz "^2.7.0" -koa-json-body@5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/koa-json-body/-/koa-json-body-5.3.0.tgz#64aad3f400adfb81df54b63f7a5eb38bad62d980" - integrity sha1-ZKrT9ACt+4HfVLY/el6zi61i2YA= - dependencies: - co-body "^5.0.0" - koa-logger@3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/koa-logger/-/koa-logger-3.2.1.tgz#ab9db879526db3837cc9ce4fd983c025b1689f22" @@ -7983,7 +7984,7 @@ qrcode@1.5.3: pngjs "^5.0.0" yargs "^15.3.1" -qs@^6.4.0, qs@^6.5.2: +qs@^6.5.2: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -8039,7 +8040,7 @@ ratelimiter@3.4.1: resolved "https://registry.yarnpkg.com/ratelimiter/-/ratelimiter-3.4.1.tgz#fa69e94937413382a926aaa17aaeaa6263af4659" integrity sha512-5FJbRW/Jkkdk29ksedAfWFkQkhbUrMx3QJGwMKAypeIiQf4yrLW+gtPKZiaWt4zPrtw1uGufOjGO7UGM6VllsQ== -raw-body@^2.2.0, raw-body@^2.3.3: +raw-body@^2.3.3: version "2.4.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== @@ -9576,7 +9577,7 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.18, type-is@^1.6.4: +type-is@^1.6.16, type-is@^1.6.18, type-is@^1.6.4: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==