Skip to content

Commit 1d3bda3

Browse files
authored
Merge pull request #95 from SOURAV-ROY/sentinel/fix-user-auth-crash-and-production-secrets-enforcement-7215157731352248001
🛡️ Sentinel: Fix potential DoS on deleted users and enforce production secrets
2 parents 1447913 + 3251cc6 commit 1d3bda3

5 files changed

Lines changed: 43 additions & 8 deletions

File tree

.jules/sentinel.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## 2026-08-14 - JWT Session Orphaning & Denial of Service (DoS) Vulnerability
2+
**Vulnerability:** Deleting a user from the database while they still possess a valid, unexpired JWT token caused the `protect` middleware to assign `req.user = null`. Downstream middlewares and routes that relied on role validation (e.g., checking `req.user.role`) would attempt to access properties of a null object, throwing a `TypeError` and causing a Denial of Service (DoS) or unexpected system crashes.
3+
**Learning:** Checking JWT validity (signature and expiration) is insufficient to guarantee that a user is still active and valid. Authenticated routes must always verify that the user fetched from the database is non-null before allowing request execution to proceed.
4+
**Prevention:** Add a database user presence check `if (!req.user)` directly in the core authentication / token-verification middleware (`protect`) before calling `next()`.

index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,30 @@ const connectDB = require("./db");
2323
// dotenv.config({path: "./config/config.env"});
2424
dotenv.config();
2525

26+
// Enforce environment secrets in production and supply secure fallbacks for development/testing
27+
if (process.env.NODE_ENV === "production") {
28+
if (!process.env.SESSION_SECRET) {
29+
throw new Error(
30+
"CRITICAL SECURITY ERROR: SESSION_SECRET is required in production mode",
31+
);
32+
}
33+
if (!process.env.JWT_SECRET) {
34+
throw new Error(
35+
"CRITICAL SECURITY ERROR: JWT_SECRET is required in production mode",
36+
);
37+
}
38+
} else {
39+
// Safe fallbacks for dev/test environments to facilitate local development and testing out-of-the-box
40+
process.env.SESSION_SECRET =
41+
process.env.SESSION_SECRET ||
42+
"dev-session-secret-placeholder-for-testing-only-12345";
43+
process.env.JWT_SECRET =
44+
process.env.JWT_SECRET ||
45+
"dev-jwt-secret-placeholder-for-testing-only-12345";
46+
process.env.JWT_EXPIRE = process.env.JWT_EXPIRE || "30d";
47+
process.env.JWT_COOKIE_EXPIRE = process.env.JWT_COOKIE_EXPIRE || "30";
48+
}
49+
2650
//Connect To DB********************************************************
2751
if (process.env.NODE_ENV !== "test") {
2852
connectDB().then(() => {

middleware/auth.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ exports.protect = asyncHandler(async (req, res, next) => {
3030

3131
req.user = await User.findById(decoded.id);
3232

33+
// Verify user still exists in database (defense in depth & prevents DoS on req.user property accesses)
34+
if (!req.user) {
35+
return next(
36+
new ErrorResponse("Not Authorized to access this route", 401),
37+
);
38+
}
39+
3340
next();
3441
} catch (errors) {
3542
return next(new ErrorResponse("Not Authorized to access this route", 401));

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"colors": "^1.4.0",
3434
"cookie-parser": "^1.4.7",
3535
"cors": "^2.8.5",
36-
"dotenv": "^16.4.5",
36+
"dotenv": "^16.6.1",
3737
"express": "^5.2.1",
3838
"express-fileupload": "^1.5.1",
3939
"express-mongo-sanitize": "^2.2.0",

0 commit comments

Comments
 (0)