From 712cfe625a19dc9790cda5fbc2843fed0836b860 Mon Sep 17 00:00:00 2001 From: Elias Schneider Date: Mon, 24 Oct 2022 12:11:10 +0200 Subject: [PATCH] feat: add rate limiting --- backend/package-lock.json | 78 +++++++++++++++++++++++++++ backend/package.json | 1 + backend/src/app.module.ts | 15 +++++- backend/src/auth/auth.controller.ts | 4 +- backend/src/main.ts | 6 ++- backend/src/share/share.controller.ts | 3 +- frontend/src/pages/api/[...all].tsx | 10 ++-- 7 files changed, 109 insertions(+), 8 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 1c257cf..7198d0e 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -16,6 +16,7 @@ "@nestjs/platform-express": "^9.1.2", "@nestjs/schedule": "^2.1.0", "@nestjs/swagger": "^6.1.2", + "@nestjs/throttler": "^3.1.0", "archiver": "^5.3.1", "argon2": "^0.29.1", "class-transformer": "^0.5.1", @@ -886,6 +887,19 @@ } } }, + "node_modules/@nestjs/throttler": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-3.1.0.tgz", + "integrity": "sha512-u9a5+rci6ybYtJ2is6gZWxE2dMZEpnK0qJ0C1OnchuNCvM21Bg6qym1TB6Uihhci+JfTv6E15WuASLXcIclsbA==", + "dependencies": { + "md5": "^2.2.1" + }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0", + "reflect-metadata": "^0.1.13" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2246,6 +2260,14 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/charset": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", @@ -2639,6 +2661,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/csv-parse": { "version": "4.16.3", "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", @@ -4110,6 +4140,11 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-core-module": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", @@ -4619,6 +4654,16 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7978,6 +8023,14 @@ "tslib": "2.4.0" } }, + "@nestjs/throttler": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-3.1.0.tgz", + "integrity": "sha512-u9a5+rci6ybYtJ2is6gZWxE2dMZEpnK0qJ0C1OnchuNCvM21Bg6qym1TB6Uihhci+JfTv6E15WuASLXcIclsbA==", + "requires": { + "md5": "^2.2.1" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9046,6 +9099,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" + }, "charset": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", @@ -9337,6 +9395,11 @@ "which": "^2.0.1" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" + }, "csv-parse": { "version": "4.16.3", "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", @@ -10461,6 +10524,11 @@ "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-core-module": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", @@ -10868,6 +10936,16 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", diff --git a/backend/package.json b/backend/package.json index 446770a..d3b41e6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,6 +18,7 @@ "@nestjs/platform-express": "^9.1.2", "@nestjs/schedule": "^2.1.0", "@nestjs/swagger": "^6.1.2", + "@nestjs/throttler": "^3.1.0", "archiver": "^5.3.1", "argon2": "^0.29.1", "class-transformer": "^0.5.1", diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 4b5a7fe..5ff2934 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -4,6 +4,8 @@ import { ScheduleModule } from "@nestjs/schedule"; import { AuthModule } from "./auth/auth.module"; import { JobsService } from "./jobs/jobs.service"; +import { APP_GUARD } from "@nestjs/core"; +import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler"; import { FileController } from "./file/file.controller"; import { FileModule } from "./file/file.module"; import { PrismaModule } from "./prisma/prisma.module"; @@ -19,9 +21,20 @@ import { UserController } from "./user/user.controller"; FileModule, PrismaModule, ConfigModule.forRoot({ isGlobal: true }), + ThrottlerModule.forRoot({ + ttl: 60, + limit: 100, + }), ScheduleModule.forRoot(), ], - providers: [PrismaService, JobsService], + providers: [ + PrismaService, + JobsService, + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, + ], controllers: [UserController, ShareController, FileController], }) export class AppModule {} diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts index 973e07d..5c722ec 100644 --- a/backend/src/auth/auth.controller.ts +++ b/backend/src/auth/auth.controller.ts @@ -6,7 +6,7 @@ import { Post, } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; - +import { Throttle } from "@nestjs/throttler"; import { AuthService } from "./auth.service"; import { AuthRegisterDTO } from "./dto/authRegister.dto"; import { AuthSignInDTO } from "./dto/authSignIn.dto"; @@ -19,6 +19,7 @@ export class AuthController { private config: ConfigService ) {} + @Throttle(10, 5 * 60) @Post("signUp") signUp(@Body() dto: AuthRegisterDTO) { if (this.config.get("ALLOW_REGISTRATION") == "false") @@ -26,6 +27,7 @@ export class AuthController { return this.authService.signUp(dto); } + @Throttle(10, 5 * 60) @Post("signIn") @HttpCode(200) signIn(@Body() dto: AuthSignInDTO) { diff --git a/backend/src/main.ts b/backend/src/main.ts index e1666df..c6bbf8a 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,12 +1,16 @@ import { ClassSerializerInterceptor, ValidationPipe } from "@nestjs/common"; import { NestFactory, Reflector } from "@nestjs/core"; +import { NestExpressApplication } from "@nestjs/platform-express"; import * as fs from "fs"; import { AppModule } from "./app.module"; + async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe()); app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); + app.set("trust proxy", true); + await fs.promises.mkdir("./data/uploads/_temp", { recursive: true }); app.setGlobalPrefix("api"); diff --git a/backend/src/share/share.controller.ts b/backend/src/share/share.controller.ts index 17d2a9b..dea6e1d 100644 --- a/backend/src/share/share.controller.ts +++ b/backend/src/share/share.controller.ts @@ -8,6 +8,7 @@ import { Post, UseGuards, } from "@nestjs/common"; +import { Throttle } from "@nestjs/throttler"; import { User } from "@prisma/client"; import { GetUser } from "src/auth/decorator/getUser.decorator"; import { JwtGuard } from "src/auth/guard/jwt.guard"; @@ -20,7 +21,6 @@ import { ShareOwnerGuard } from "./guard/shareOwner.guard"; import { ShareSecurityGuard } from "./guard/shareSecurity.guard"; import { ShareTokenSecurity } from "./guard/shareTokenSecurity.guard"; import { ShareService } from "./share.service"; - @Controller("shares") export class ShareController { constructor(private shareService: ShareService) {} @@ -70,6 +70,7 @@ export class ShareController { } @HttpCode(200) + @Throttle(10, 5 * 60) @UseGuards(ShareTokenSecurity) @Post(":id/token") async getShareToken(@Param("id") id: string, @Body() body: SharePasswordDto) { diff --git a/frontend/src/pages/api/[...all].tsx b/frontend/src/pages/api/[...all].tsx index 4a5698a..ed98549 100644 --- a/frontend/src/pages/api/[...all].tsx +++ b/frontend/src/pages/api/[...all].tsx @@ -8,9 +8,11 @@ export const config = { }, }; -// This function can be marked `async` if using `await` inside -export default (req: NextApiRequest, res: NextApiResponse) => - httpProxyMiddleware(req, res, { - // You can use the `http-proxy` option +export default (req: NextApiRequest, res: NextApiResponse) => { + return httpProxyMiddleware(req, res, { + headers: { + "X-Forwarded-For": req.socket.remoteAddress ?? "", + }, target: "http://localhost:8080", }); +};