docs(auth): add password security hardening plan and code markers
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user