add strict rate limit to endpoints that send email

This commit is contained in:
Milo Schwartz 2025-02-05 22:46:33 -05:00
parent 58a084426b
commit 3c7025a327
No known key found for this signature in database
2 changed files with 46 additions and 5 deletions

View file

@ -8,10 +8,8 @@ import { db } from "@server/db";
import { passwordResetTokens, users } from "@server/db/schema"; import { passwordResetTokens, users } from "@server/db/schema";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { alphabet, generateRandomString, sha256 } from "oslo/crypto"; import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
import { encodeHex } from "oslo/encoding";
import { createDate } from "oslo"; import { createDate } from "oslo";
import logger from "@server/logger"; import logger from "@server/logger";
import { generateIdFromEntropySize } from "@server/auth/sessions/app";
import { TimeSpan } from "oslo"; import { TimeSpan } from "oslo";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { sendEmail } from "@server/emails"; import { sendEmail } from "@server/emails";
@ -85,7 +83,9 @@ export async function requestPasswordReset(
const url = `${config.getRawConfig().app.dashboard_url}/auth/reset-password?email=${email}&token=${token}`; const url = `${config.getRawConfig().app.dashboard_url}/auth/reset-password?email=${email}&token=${token}`;
if (!config.getRawConfig().email) { if (!config.getRawConfig().email) {
logger.info(`Password reset requested for ${email}. Token: ${token}.`); logger.info(
`Password reset requested for ${email}. Token: ${token}.`
);
} }
await sendEmail( await sendEmail(

View file

@ -27,6 +27,8 @@ import { verifyUserHasAction } from "../middlewares/verifyUserHasAction";
import { ActionsEnum } from "@server/auth/actions"; import { ActionsEnum } from "@server/auth/actions";
import { verifyUserIsOrgOwner } from "../middlewares/verifyUserIsOrgOwner"; import { verifyUserIsOrgOwner } from "../middlewares/verifyUserIsOrgOwner";
import { createNewt, getToken } from "./newt"; import { createNewt, getToken } from "./newt";
import rateLimit from "express-rate-limit";
import createHttpError from "http-errors";
// Root routes // Root routes
export const unauthenticated = Router(); export const unauthenticated = Router();
@ -452,22 +454,61 @@ authRouter.post(
); );
authRouter.post("/2fa/disable", verifySessionUserMiddleware, auth.disable2fa); authRouter.post("/2fa/disable", verifySessionUserMiddleware, auth.disable2fa);
authRouter.post("/verify-email", verifySessionMiddleware, auth.verifyEmail); authRouter.post("/verify-email", verifySessionMiddleware, auth.verifyEmail);
authRouter.post( authRouter.post(
"/verify-email/request", "/verify-email/request",
verifySessionMiddleware, verifySessionMiddleware,
rateLimit({
windowMs: 15 * 60 * 1000,
max: 3,
keyGenerator: (req) => `requestEmailVerificationCode:${req.body.email}`,
handler: (req, res, next) => {
const message = `You can only request an email verification code ${3} times every ${15} minutes. Please try again later.`;
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
}
}),
auth.requestEmailVerificationCode auth.requestEmailVerificationCode
); );
// authRouter.post( // authRouter.post(
// "/change-password", // "/change-password",
// verifySessionUserMiddleware, // verifySessionUserMiddleware,
// auth.changePassword // auth.changePassword
// ); // );
authRouter.post("/reset-password/request", auth.requestPasswordReset);
authRouter.post(
"/reset-password/request",
rateLimit({
windowMs: 15 * 60 * 1000,
max: 3,
keyGenerator: (req) => `requestPasswordReset:${req.body.email}`,
handler: (req, res, next) => {
const message = `You can only request a password reset ${3} times every ${15} minutes. Please try again later.`;
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
}
}),
auth.requestPasswordReset
);
authRouter.post("/reset-password/", auth.resetPassword); authRouter.post("/reset-password/", auth.resetPassword);
authRouter.post("/resource/:resourceId/password", resource.authWithPassword); authRouter.post("/resource/:resourceId/password", resource.authWithPassword);
authRouter.post("/resource/:resourceId/pincode", resource.authWithPincode); authRouter.post("/resource/:resourceId/pincode", resource.authWithPincode);
authRouter.post("/resource/:resourceId/whitelist", resource.authWithWhitelist);
authRouter.post(
"/resource/:resourceId/whitelist",
rateLimit({
windowMs: 15 * 60 * 1000,
max: 10,
keyGenerator: (req) => `authWithWhitelist:${req.body.email}`,
handler: (req, res, next) => {
const message = `You can only request an email OTP ${10} times every ${15} minutes. Please try again later.`;
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
}
}),
resource.authWithWhitelist
);
authRouter.post( authRouter.post(
"/resource/:resourceId/access-token", "/resource/:resourceId/access-token",
resource.authWithAccessToken resource.authWithAccessToken