Skip to content

Commit

Permalink
HTTP Signare 検証の修正 (#2719)
Browse files Browse the repository at this point in the history
* validate signed headers

* validate signed headers

* リクエストホスト

Co-authored-by: perillamint <[email protected]>
Co-authored-by: yunochi <[email protected]>
Co-authored-by: Laura Hausmann <[email protected]>

* fix

* test

* Validate signature algorithm

* a

---------

Co-authored-by: perillamint <[email protected]>
Co-authored-by: yunochi <[email protected]>
Co-authored-by: Laura Hausmann <[email protected]>
  • Loading branch information
4 people authored Dec 2, 2023
1 parent a97e3a3 commit b008e1e
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 42 deletions.
3 changes: 2 additions & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"chalk-template": "0.4.0",
"chokidar": "3.5.3",
"cli-highlight": "2.1.11",
"co-body": "6.1.0",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
"date-fns": "2.29.2",
Expand All @@ -62,7 +63,6 @@
"koa": "2.13.4",
"koa-bodyparser": "4.3.0",
"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",
Expand Down Expand Up @@ -126,6 +126,7 @@
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.15.9",
"@types/cbor": "6.0.0",
"@types/co-body": "6.1.3",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.20",
"@types/js-yaml": "4.0.5",
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/@types/koa-json-body.d.ts

This file was deleted.

93 changes: 86 additions & 7 deletions packages/backend/src/server/activitypub.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import Router from '@koa/router';
import json from 'koa-json-body';
import config from '@/config/index.js';
import * as coBody from 'co-body';
import * as crypto from 'node:crypto';
import { IActivity } from '@/remote/activitypub/type.js';
import httpSignature from '@peertube/http-signature';
import Logger from '@/services/logger.js';
import { inspect } from 'util';

import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderNote from '@/remote/activitypub/renderer/note.js';
Expand All @@ -20,22 +25,96 @@ import { renderLike } from '@/remote/activitypub/renderer/like.js';
import { getUserKeypair } from '@/misc/keypair-store.js';
import renderFollow from '@/remote/activitypub/renderer/follow.js';

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) {
logger.warn(`inbox: Invalid Host`);
ctx.status = 400;
ctx.message = 'Invalid Host';
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;

if (e instanceof Error) {
if (e.name === 'ExpiredRequestError') ctx.message = 'Expired Request Error';
if (e.name === 'MissingHeaderError') ctx.message = 'Missing Required Header';
}

return;
}

// Validate signature algorithm
if (!signature.algorithm.toLowerCase().match(/^((dsa|rsa|ecdsa)-(sha256|sha384|sha512)|ed25519-sha512|hs2019)$/)) {
logger.warn(`inbox: invalid signature algorithm ${signature.algorithm}`);
ctx.status = 401;
ctx.message = 'Invalid Signature Algorithm';
return;

// hs2019
// keyType=ED25519 => ed25519-sha512
// keyType=other => (keyType)-sha256
}

// Digestヘッダーの検証
const digest = ctx.req.headers.digest;

// 無いとか複数あるとかダメ!
if (typeof digest !== 'string') {
logger.warn(`inbox: unrecognized digest header 1`);
ctx.status = 401;
ctx.message = 'Invalid Digest Header';
return;
}

const match = digest.match(/^([0-9A-Za-z-]+)=(.+)$/);

if (match == null) {
logger.warn(`inbox: unrecognized digest header 2`);
ctx.status = 401;
ctx.message = 'Invalid Digest Header';
return;
}

const digestAlgo = match[1];
const digestExpected = match[2];

if (digestAlgo.toUpperCase() !== 'SHA-256') {
logger.warn(`inbox: Unsupported Digest Algorithm`);
ctx.status = 401;
ctx.message = 'Unsupported Digest Algorithm';
return;
}

const digestActual = crypto.createHash('sha256').update(raw).digest('base64');

if (digestExpected !== digestActual) {
logger.warn(`inbox: Digest Missmatch`);
ctx.status = 401;
ctx.message = 'Digest Missmatch';
return;
}

processInbox(ctx.request.body, signature);
processInbox(ctx.request.body as IActivity, signature);

ctx.status = 202;
}
Expand All @@ -59,8 +138,8 @@ export function setResponseType(ctx: Router.RouterContext) {
}

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

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

0 comments on commit b008e1e

Please sign in to comment.