From 37f3cc332506ed29545bcc604da324b8f8348c10 Mon Sep 17 00:00:00 2001 From: Bogdan Mihalcea Date: Wed, 27 Mar 2024 10:16:52 +0200 Subject: [PATCH] fixed password reset vulnerability --- .gitignore | 3 +++ node-api/src/routes/index.js | 3 +++ node-api/src/schemas/passwordResets.schema.js | 15 +++++++++++++++ node-api/src/services/auth/index.js | 17 +++++++++++++---- 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 .gitignore create mode 100644 node-api/src/schemas/passwordResets.schema.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f4f7d324 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +branch_structure.json +temp_auto_push.bat +temp_interactive_push.bat diff --git a/node-api/src/routes/index.js b/node-api/src/routes/index.js index 663cb637..0ad782cc 100644 --- a/node-api/src/routes/index.js +++ b/node-api/src/routes/index.js @@ -1,5 +1,8 @@ import userRoutes from './users'; import meRoutes from './me'; import authRoutes from './auth'; +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); export { userRoutes, meRoutes, authRoutes }; diff --git a/node-api/src/schemas/passwordResets.schema.js b/node-api/src/schemas/passwordResets.schema.js new file mode 100644 index 00000000..c21c019c --- /dev/null +++ b/node-api/src/schemas/passwordResets.schema.js @@ -0,0 +1,15 @@ +import mongoose from "mongoose"; + +const passwordResetSchema = new mongoose.Schema({ + email: { required: true, type: String }, + token: { required: true, type: String }, + created_at: { type: Date }, +}); + +passwordResetSchema.virtual("id").get(function () { + return this._id.toHexString(); +}); + +passwordResetSchema.set("toJSON", { virtuals: true }); + +export const passwordResetModel = mongoose.model("PasswordReset", passwordResetSchema); diff --git a/node-api/src/services/auth/index.js b/node-api/src/services/auth/index.js index ba749cd9..84965d61 100644 --- a/node-api/src/services/auth/index.js +++ b/node-api/src/services/auth/index.js @@ -3,6 +3,7 @@ import nodemailer from "nodemailer"; import randomToken from "random-token"; import bcrypt from "bcrypt"; import { userModel } from "../../schemas/user.schema"; +import { passwordResetModel } from "../../schemas/passwordResets.schema"; import jwt from 'jsonwebtoken'; dotenv.config(); @@ -109,6 +110,14 @@ export const forgotPasswordRouteHandler = async (req, res, email) => { email: email, }, }; + + // save token in db + await passwordResetModel.create({ + email: foundUser.email, + token: token, + created_at: new Date(), + }); + return res.status(204).json(dataSent); } }; @@ -118,10 +127,8 @@ export const resetPasswordRouteHandler = async (req, res) => { email: req.body.data.attributes.email, }); - if (!foundUser) { - return res.status(400).json({ - errors: { email: ["The email does not match any existing user."] }, - }); + if (!foundUser || !foundToken) { + return res.status(400).json({errors: { email: ["The email or token does not match any existing user."] }}); } else { const { password, password_confirmation } = req.body.data.attributes; // validate password @@ -143,6 +150,8 @@ export const resetPasswordRouteHandler = async (req, res) => { const salt = await bcrypt.genSalt(10); const hashPassword = await bcrypt.hash(password, salt); + await passwordResetModel.deleteOne({ email: foundUser.email }); + await userModel.updateOne( { email: foundUser.email }, { $set: { "password": hashPassword } }