From 88afab6571ef7d4d41bb395cdb6ecd3968835a4a Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 9 Oct 2023 07:01:54 +0800 Subject: [PATCH] Merge pull request from GHSA-g9v2-wqcj-j99g * Fix attempt * Update message --- server/model/user.js | 15 +++++++++++++++ server/server.js | 23 ++++++++++++++--------- server/util-server.js | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/server/model/user.js b/server/model/user.js index fc619c74b5..32a5a9613f 100644 --- a/server/model/user.js +++ b/server/model/user.js @@ -1,6 +1,8 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); const passwordHash = require("../password-hash"); const { R } = require("redbean-node"); +const jwt = require("jsonwebtoken"); +const { shake256, SHAKE256_LENGTH } = require("../util-server"); class User extends BeanModel { /** @@ -27,6 +29,19 @@ class User extends BeanModel { this.password = newPassword; } + /** + * Create a new JWT for a user + * @param {User} user + * @param {string} jwtSecret + * @return {string} + */ + static createJWT(user, jwtSecret) { + return jwt.sign({ + username: user.username, + h: shake256(user.password, SHAKE256_LENGTH), + }, jwtSecret); + } + } module.exports = User; diff --git a/server/server.js b/server/server.js index 305077f5a9..c8f826b028 100644 --- a/server/server.js +++ b/server/server.js @@ -83,8 +83,11 @@ const app = server.app; log.info("server", "Importing this project modules"); log.debug("server", "Importing Monitor"); const Monitor = require("./model/monitor"); +const User = require("./model/user"); + log.debug("server", "Importing Settings"); -const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword, startE2eTests } = require("./util-server"); +const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword, startE2eTests, shake256, SHAKE256_LENGTH +} = require("./util-server"); log.debug("server", "Importing Notification"); const { Notification } = require("./notification"); @@ -296,6 +299,11 @@ let needSetup = false; decoded.username, ]); + // Check if the password changed + if (decoded.h !== shake256(user.password, SHAKE256_LENGTH)) { + throw new Error("The token is invalid due to password change or old token"); + } + if (user) { log.debug("auth", "afterLogin"); afterLogin(socket, user); @@ -316,9 +324,10 @@ let needSetup = false; }); } } catch (error) { - log.error("auth", `Invalid token. IP=${clientIP}`); - + if (error.message) { + log.error("auth", error.message, `IP=${clientIP}`); + } callback({ ok: false, msg: "Invalid token.", @@ -357,9 +366,7 @@ let needSetup = false; callback({ ok: true, - token: jwt.sign({ - username: data.username, - }, server.jwtSecret), + token: User.createJWT(user, server.jwtSecret), }); } @@ -387,9 +394,7 @@ let needSetup = false; callback({ ok: true, - token: jwt.sign({ - username: data.username, - }, server.jwtSecret), + token: User.createJWT(user, server.jwtSecret), }); } else { diff --git a/server/util-server.js b/server/util-server.js index fe61e10abe..0cb63ad000 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -33,6 +33,7 @@ const dayjs = require("dayjs"); // SASLOptions used in JSDoc // eslint-disable-next-line no-unused-vars const { Kafka, SASLOptions } = require("kafkajs"); +const crypto = require("crypto"); const isWindows = process.platform === /^win/.test(process.platform); /** @@ -1055,6 +1056,23 @@ module.exports.grpcQuery = async (options) => { }); }; +module.exports.SHAKE256_LENGTH = 16; + +/** + * + * @param {string} data + * @param {number} len + * @return {string} + */ +module.exports.shake256 = (data, len) => { + if (!data) { + return ""; + } + return crypto.createHash("shake256", { outputLength: len }) + .update(data) + .digest("hex"); +}; + // For unit test, export functions if (process.env.TEST_BACKEND) { module.exports.__test = {