diff --git a/server/.env.test b/server/.env.test index e75f7f5..f986d1d 100644 --- a/server/.env.test +++ b/server/.env.test @@ -2,7 +2,7 @@ DATABASE_URL="file:./test.sqlite" FRONTEND_URL=https://example.com POST_LIMIT=50 -POST_LIMIT_WINDOW_SECONDS=0.1 +POST_LIMIT_WINDOW_SECONDS=0.25 GET_LIMIT=20 GET_LIMIT_WINDOW_SECONDS=0.1 LOG_LEVEL=warn diff --git a/server/src/app.integration.test.ts b/server/src/app.integration.test.ts index e6cb344..084fe57 100644 --- a/server/src/app.integration.test.ts +++ b/server/src/app.integration.test.ts @@ -152,7 +152,7 @@ describe("POST /api/note", () => { it("Applies rate limits to endpoint", async () => { // make more requests than the post limit set in .env.test const requests = []; - for (let i = 0; i < 52; i++) { + for (let i = 0; i < 51; i++) { requests.push(request(app).post("/api/note").send(testNote)); } const responses = await Promise.all(requests); @@ -162,7 +162,7 @@ describe("POST /api/note", () => { expect(responseCodes).toContain(429); // sleep for 100 ms to allow rate limiter to reset - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 250)); }); }); diff --git a/server/src/app.ts b/server/src/app.ts index 378ebe2..b6b8a89 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -1,7 +1,7 @@ import "dotenv/config"; import express, { Express, Request, Response } from "express"; import { EncryptedNote } from "@prisma/client"; -import { addDays } from "./util"; +import { addDays, getConnectingIp } from "./util"; import helmet from "helmet"; import rateLimit from "express-rate-limit"; import pinoHttp from "pino-http"; @@ -52,8 +52,7 @@ app.use(bodyParser.json({ limit: "400k" })); // Get encrypted note app.get("/api/note/:id", getLimiter, (req: Request, res: Response, next) => { - const ip = (req.headers["x-forwarded-for"] || - req.socket.remoteAddress) as string; + const ip = getConnectingIp(req); prisma.encryptedNote .findUnique({ where: { id: req.params.id }, @@ -90,8 +89,8 @@ app.get("/api/note/:id", getLimiter, (req: Request, res: Response, next) => { // Post new encrypted note app.post("/api/note/", postLimiter, (req: Request, res: Response, next) => { - const ip = (req.headers["x-forwarded-for"] || - req.socket.remoteAddress) as string; + const ip = getConnectingIp(req); + const notePostRequest = new NotePostRequest(); Object.assign(notePostRequest, req.body); validateOrReject(notePostRequest).catch((err) => { @@ -132,7 +131,6 @@ app.post("/api/note/", postLimiter, (req: Request, res: Response, next) => { // Clean up expired notes periodically export async function cleanExpiredNotes(): Promise { logger.info("[Cleanup] Cleaning up expired notes..."); - const toDelete = await prisma.encryptedNote.findMany({ where: { expire_time: { diff --git a/server/src/util.ts b/server/src/util.ts index f0d8b08..01b03cf 100644 --- a/server/src/util.ts +++ b/server/src/util.ts @@ -1,5 +1,13 @@ +import { Request } from "express"; + export function addDays(date: Date, days: number): Date { var result = new Date(date); result.setDate(result.getDate() + days); return result; } + +export function getConnectingIp(req: Request): string { + return (req.headers["cf-connecting-ip"] || + req.headers["X-Forwarded-For"] || + req.socket.remoteAddress) as string; +} diff --git a/webapp/src/routes/note/[id].ts b/webapp/src/routes/note/[id].ts index 87c7afc..c4b7c64 100644 --- a/webapp/src/routes/note/[id].ts +++ b/webapp/src/routes/note/[id].ts @@ -2,7 +2,9 @@ import type { EncryptedNote } from '$lib/model/EncryptedNote'; import type { RequestHandler } from '@sveltejs/kit'; export const get: RequestHandler = async ({ request, clientAddress, params }) => { - const ip = (request.headers.get('x-forwarded-for') || clientAddress) as string; + const ip = (request.headers.get('cd-connecting-ip') || + request.headers.get('x-forwarded-for') || + clientAddress) as string; const url = `${import.meta.env.VITE_SERVER_INTERNAL}/api/note/${params.id}`; const response = await fetch(url, { headers: {