From 2be1a39d1375f50ce4dbdbb336936b17cd59445f Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 7 Feb 2023 19:58:58 +0900 Subject: [PATCH] fix(server): validate urls from ap to improve security --- CHANGELOG.md | 1 + .../core/activitypub/models/ApNoteService.ts | 17 +++++++++++++---- .../core/activitypub/models/ApPersonService.ts | 16 ++++++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c99e8f16..1514de895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ You should also include the user name that made the change. ### Bugfixes - Client: MkEmojiPickerでもChromeで検索ダイアログで変換確定するとそのまま検索されてしまうのを修正 +- fix(server): validate urls from ap to improve security ## 13.4.0 (2023/02/05) diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index c9192f53b..813415e6f 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -1,8 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; -import type { MessagingMessagesRepository, PollsRepository, EmojisRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { MessagingMessagesRepository, PollsRepository, EmojisRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; @@ -18,6 +17,7 @@ import { PollService } from '@/core/PollService.js'; import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; import { MessagingService } from '@/core/MessagingService.js'; +import { bindThis } from '@/decorators.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import { ApLoggerService } from '../ApLoggerService.js'; @@ -32,7 +32,6 @@ import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ApNoteService { @@ -133,6 +132,16 @@ export class ApNoteService { const note: IPost = object; this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); + + if (note.id && !note.id.startsWith('https://')) { + throw new Error('unexpected shcema of note.id: ' + note.id); + } + + const url = getOneApHrefNullable(note.url); + + if (url && !url.startsWith('https://')) { + throw new Error('unexpected shcema of note url: ' + url); + } this.logger.info(`Creating the Note: ${note.id}`); @@ -307,7 +316,7 @@ export class ApNoteService { apEmojis, poll, uri: note.id, - url: getOneApHrefNullable(note.url), + url: url, }, silent); } diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 2325bbe09..76f820cda 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -252,6 +252,12 @@ export class ApPersonService implements OnModuleInit { const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); + const url = getOneApHrefNullable(person.url); + + if (url && !url.startsWith('https://')) { + throw new Error('unexpected shcema of person url: ' + url); + } + // Create user let user: IRemoteUser; try { @@ -283,7 +289,7 @@ export class ApPersonService implements OnModuleInit { await transactionalEntityManager.save(new UserProfile({ userId: user.id, description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, - url: getOneApHrefNullable(person.url), + url: url, fields, birthday: bday ? bday[0] : null, location: person['vcard:Address'] ?? null, @@ -425,6 +431,12 @@ export class ApPersonService implements OnModuleInit { const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); + const url = getOneApHrefNullable(person.url); + + if (url && !url.startsWith('https://')) { + throw new Error('unexpected shcema of person url: ' + url); + } + const updates = { lastFetchedAt: new Date(), inbox: person.inbox, @@ -459,7 +471,7 @@ export class ApPersonService implements OnModuleInit { } await this.userProfilesRepository.update({ userId: exist.id }, { - url: getOneApHrefNullable(person.url), + url: url, fields, description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, birthday: bday ? bday[0] : null,