docs(auth): add password security hardening plan and code markers

This commit is contained in:
Adolfo Reyna
2026-02-20 20:07:26 -05:00
parent ea864b27d4
commit 0baf237548
4 changed files with 113 additions and 5 deletions

View File

@@ -14,6 +14,9 @@ const Profile = require("../def/profile.js");
// When new users are subscribed, they have a single profile, which is the personal one.
// Other profiles can be link to that user, like groups or courses.
const signup = async function (req, res) {
// SECURITY PLAN (point #2):
// Stop accepting credentials from query params and enforce POST body only.
// Query-string credentials can leak through logs/history/referers.
const username = req.query.username || req.body.username;
const password = req.query.password || req.body.password;
const email = req.query.email || req.body.email;
@@ -34,8 +37,9 @@ const signup = async function (req, res) {
}
let isUserAlreadyRegistered = await DB.getUser(email);
if (isUserAlreadyRegistered && isUserAlreadyRegistered._id) return res.json({ status: "This user is already registered" });
// Hash password to be stored on the DB.
// TODO: I think this is missing a Salt factor to improve security
// SECURITY PLAN (point #5):
// bcrypt.hash already includes a per-password salt.
// Future hardening: centralize cost factor policy (and consider rehash-on-login).
const hashedPassword = await bcrypt.hash(password, 10);
const newUserObject = await DB.newUser({
username: username.toLowerCase(),
@@ -79,9 +83,13 @@ const login = async function (req, res) {
const userInfo = await DB.checkSessionOnDB(session_id, user_sid);
if (userInfo) return res.redirect('/');
}
// SECURITY PLAN (point #2):
// Migrate to req.body-only credentials after route-level POST-only enforcement.
const username = req.body.username || req.query.username;
const password = req.body.password || req.query.password || "";
const user = await DB.getUser(username);
// SECURITY PLAN (point #4):
// Replace user-existence specific response with generic invalid-credentials response.
if (!user) {
client_logger.capture({
distinctId: 'app_level',
@@ -92,8 +100,11 @@ const login = async function (req, res) {
});
return res.json({ status: "user not founded" });
}
// TODO: Also add salt parameter here.
// SECURITY PLAN (point #5):
// bcrypt.compare validates salted hashes directly; no manual salt parameter is needed.
const isSamePassword = await bcrypt.compare(password, user.password);
// SECURITY PLAN (point #4):
// Use the same generic auth error response for wrong password and unknown user.
if (!isSamePassword) return res.json({ status: "incorrect password" });
try {
// Store a new session loging on DB, and use ID as session ID
@@ -178,6 +189,12 @@ const resetPassword = async function (req, res) {
});
}
}
// SECURITY PLAN (point #1):
// Replace this entire non-authenticated branch with reset-token flow:
// request-reset(email) -> email link -> confirm-reset(token, newPassword).
// Do not generate/send plaintext passwords by email.
// SECURITY PLAN (point #4):
// Return generic response regardless of account existence to avoid enumeration.
// Logic for non-logged in users.
const username = req.body.username;
const user = await DB.getUser(username);
@@ -215,4 +232,4 @@ module.exports = {
login,
logout,
resetPassword,
}
}