fix(server): "forkbomb" DOS mitigation (#9247)
* Add recursion limit to resolver * Use shared resolver in featured and question * Changelog * Changelog fix * Update CHANGELOG.md Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> * Add host to recursion limit error message Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
This commit is contained in:
		
							parent
							
								
									5decad9cf1
								
							
						
					
					
						commit
						66513b9893
					
				
					 5 changed files with 14 additions and 8 deletions
				
			
		|  | @ -25,6 +25,7 @@ You should also include the user name that made the change. | |||
| - Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468 | ||||
| - Server: Bug fix for Pinned Users lookup on instance @squidicuzz | ||||
| - Client: インスタンスティッカーのfaviconを読み込む際に偽サイト警告が出ることがあるのを修正 @syuilo | ||||
| - Server: Mitigate AP reference chain DoS vector @skehmatics | ||||
| 
 | ||||
| ## 12.119.0 (2022/09/10) | ||||
| 
 | ||||
|  |  | |||
|  | @ -731,7 +731,7 @@ export class ApInboxService { | |||
| 			await this.apPersonService.updatePerson(actor.uri!, resolver, object); | ||||
| 			return 'ok: Person updated'; | ||||
| 		} else if (getApType(object) === 'Question') { | ||||
| 			await this.apQuestionService.updateQuestion(object).catch(err => console.error(err)); | ||||
| 			await this.apQuestionService.updateQuestion(object, resolver).catch(err => console.error(err)); | ||||
| 			return 'ok: Question updated'; | ||||
| 		} else { | ||||
| 			return `skip: Unknown type: ${getApType(object)}`; | ||||
|  |  | |||
|  | @ -76,6 +76,7 @@ export class Resolver { | |||
| 		private httpRequestService: HttpRequestService, | ||||
| 		private apRendererService: ApRendererService, | ||||
| 		private apDbResolverService: ApDbResolverService, | ||||
| 		private recursionLimit = 100 | ||||
| 	) { | ||||
| 		this.history = new Set(); | ||||
| 	} | ||||
|  | @ -116,6 +117,10 @@ export class Resolver { | |||
| 			throw new Error('cannot resolve already resolved one'); | ||||
| 		} | ||||
| 
 | ||||
| 		if (this.history.size > this.recursionLimit) { | ||||
| 			throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); | ||||
| 		} | ||||
| 
 | ||||
| 		this.history.add(value); | ||||
| 
 | ||||
| 		const host = this.utilityService.extractDbHost(value); | ||||
|  |  | |||
|  | @ -390,7 +390,7 @@ export class ApPersonService implements OnModuleInit { | |||
| 	}); | ||||
| 	//#endregion
 | ||||
| 
 | ||||
| 	await this.updateFeatured(user!.id).catch(err => this.logger.error(err)); | ||||
| 	await this.updateFeatured(user!.id, resolver).catch(err => this.logger.error(err)); | ||||
| 
 | ||||
| 	return user!; | ||||
| 	} | ||||
|  | @ -503,7 +503,7 @@ export class ApPersonService implements OnModuleInit { | |||
| 			followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined), | ||||
| 		}); | ||||
| 
 | ||||
| 		await this.updateFeatured(exist.id).catch(err => this.logger.error(err)); | ||||
| 		await this.updateFeatured(exist.id, resolver).catch(err => this.logger.error(err)); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
|  | @ -551,14 +551,14 @@ export class ApPersonService implements OnModuleInit { | |||
| 		return { fields, services }; | ||||
| 	} | ||||
| 
 | ||||
| 	public async updateFeatured(userId: User['id']) { | ||||
| 	public async updateFeatured(userId: User['id'], resolver?: Resolver) { | ||||
| 		const user = await this.usersRepository.findOneByOrFail({ id: userId }); | ||||
| 		if (!this.userEntityService.isRemoteUser(user)) return; | ||||
| 		if (!user.featured) return; | ||||
| 
 | ||||
| 		this.logger.info(`Updating the featured: ${user.uri}`); | ||||
| 
 | ||||
| 		const resolver = this.apResolverService.createResolver(); | ||||
| 		if (resolver == null) resolver = this.apResolverService.createResolver(); | ||||
| 
 | ||||
| 		// Resolve to (Ordered)Collection Object
 | ||||
| 		const collection = await resolver.resolveCollection(user.featured); | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ export class ApQuestionService { | |||
| 	 * @param uri URI of AP Question object | ||||
| 	 * @returns true if updated | ||||
| 	 */ | ||||
| 	public async updateQuestion(value: any) { | ||||
| 	public async updateQuestion(value: any, resolver?: Resolver) { | ||||
| 		const uri = typeof value === 'string' ? value : value.id; | ||||
| 
 | ||||
| 		// URIがこのサーバーを指しているならスキップ
 | ||||
|  | @ -80,7 +80,7 @@ export class ApQuestionService { | |||
| 		//#endregion
 | ||||
| 
 | ||||
| 		// resolve new Question object
 | ||||
| 		const resolver = this.apResolverService.createResolver(); | ||||
| 		if (resolver == null) resolver = this.apResolverService.createResolver(); | ||||
| 		const question = await resolver.resolve(value) as IQuestion; | ||||
| 		this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue