Fix IP address rate limit (#8758)
* Fix IP address rate limit * CHANGELOG * Tune getIpHash
This commit is contained in:
		
							parent
							
								
									a98194bf1b
								
							
						
					
					
						commit
						c05723ca6a
					
				
					 4 changed files with 15 additions and 10 deletions
				
			
		|  | @ -26,7 +26,7 @@ You should also include the user name that made the change. | |||
|   Your own theme color may be unset if it was in an invalid format. | ||||
|   Admins should check their instance settings if in doubt. | ||||
| - Perform port diagnosis at startup only when Listen fails @mei23 | ||||
| - Rate limiting is now also usable for non-authenticated users. @Johann150 | ||||
| - Rate limiting is now also usable for non-authenticated users. @Johann150 @mei23 | ||||
|   Admins should make sure the reverse proxy sets the `X-Forwarded-For` header to the original address. | ||||
| 
 | ||||
| ### Bugfixes | ||||
|  |  | |||
							
								
								
									
										9
									
								
								packages/backend/src/misc/get-ip-hash.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/backend/src/misc/get-ip-hash.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| import IPCIDR from 'ip-cidr'; | ||||
| 
 | ||||
| export function getIpHash(ip: string) { | ||||
| 	// because a single person may control many IPv6 addresses,
 | ||||
| 	// only a /64 subnet prefix of any IP will be taken into account.
 | ||||
| 	// (this means for IPv4 the entire address is used)
 | ||||
| 	const prefix = IPCIDR.createAddress(ip).mask(64); | ||||
| 	return 'ip-' + BigInt('0b' + prefix).toString(36); | ||||
| } | ||||
|  | @ -6,7 +6,7 @@ import endpoints, { IEndpointMeta } from './endpoints.js'; | |||
| import { ApiError } from './error.js'; | ||||
| import { apiLogger } from './logger.js'; | ||||
| import { AccessToken } from '@/models/entities/access-token.js'; | ||||
| import IPCIDR from 'ip-cidr'; | ||||
| import { getIpHash } from '@/misc/get-ip-hash.js'; | ||||
| 
 | ||||
| const accessDenied = { | ||||
| 	message: 'Access denied.', | ||||
|  | @ -33,18 +33,13 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi | |||
| 		throw new ApiError(accessDenied); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ep.meta.requireCredential && ep.meta.limit && !isModerator) { | ||||
| 	if (ep.meta.limit && !isModerator) { | ||||
| 		// koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app.
 | ||||
| 		let limitActor: string; | ||||
| 		if (user) { | ||||
| 			limitActor = user.id; | ||||
| 		} else { | ||||
| 			// because a single person may control many IPv6 addresses,
 | ||||
| 			// only a /64 subnet prefix of any IP will be taken into account.
 | ||||
| 			// (this means for IPv4 the entire address is used)
 | ||||
| 			const ip = IPCIDR.createAddress(ctx.ip).mask(64); | ||||
| 
 | ||||
| 			limitActor = 'ip-' + parseInt(ip, 2).toString(36); | ||||
| 			limitActor = getIpHash(ctx!.ip); | ||||
| 		} | ||||
| 
 | ||||
| 		const limit = Object.assign({}, ep.meta.limit); | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import { verifyLogin, hash } from '../2fa.js'; | |||
| import { randomBytes } from 'node:crypto'; | ||||
| import { IsNull } from 'typeorm'; | ||||
| import { limiter } from '../limiter.js'; | ||||
| import { getIpHash } from '@/misc/get-ip-hash.js'; | ||||
| 
 | ||||
| export default async (ctx: Koa.Context) => { | ||||
| 	ctx.set('Access-Control-Allow-Origin', config.url); | ||||
|  | @ -27,7 +28,7 @@ export default async (ctx: Koa.Context) => { | |||
| 
 | ||||
| 	try { | ||||
| 		// not more than 1 attempt per second and not more than 10 attempts per hour
 | ||||
| 		await limiter({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, ctx.ip); | ||||
| 		await limiter({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(ctx.ip)); | ||||
| 	} catch (err) { | ||||
| 		ctx.status = 429; | ||||
| 		ctx.body = { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue