This document explains all improvements (10 points) that were applied to the Backend-Template-JavaScript project to increase security and performance according to common best practices used in professional projects.
- The server startup sequence was improved to ensure the server does not start before a successful MongoDB connection.
- The security layer was strengthened using essential middlewares (Security Headers / Rate Limiting / Sanitization).
- Cookies and tokens handling was hardened (Cookie-only auth) and Refresh Tokens security was improved with hashing + rotation + reuse detection.
- File uploads (Multer) were hardened to prevent unsafe/large uploads and to prevent direct access to temporary files.
- Error handling was unified by adding a 404 handler and a global error handler.
- Sensitive logging was removed and replaced with a professional logger with redaction to prevent leaking sensitive data.
The server could start (app.listen) before ensuring a successful database connection, which can cause runtime errors when requests depend on MongoDB.
app.jswas made responsible only for configuring middlewares/routes and exportingapp.- The server start (
listen) was moved toindex.jsafterconnectDB()succeeds.
src/app.jssrc/index.js
- Disconnect MongoDB or set a wrong URI → the server should not start normally.
- When DB succeeds → the server starts and prints the startup message.
The project lacked essential HTTP security headers (e.g., preventing sniffing, clickjacking protection, referrer policy, etc.).
helmet was added to enable secure headers with API-friendly settings, and x-powered-by was disabled to reduce information disclosure.
src/app.jspackage.json
- Call any endpoint and check the response headers (e.g.,
X-Content-Type-Optionsand others).
Auth endpoints are commonly targeted by:
- Brute force attacks
- Credential stuffing
- DoS on sensitive routes
express-rate-limit was added and a dedicated limiter was applied to:
/register/login/refresh-token
(The limiter was placed before upload on register to reduce resource usage under abuse.)
src/middlewares/rateLimit.middleware.jssrc/routes/user.routes.jspackage.json
- Repeat login requests rapidly above the configured limit → you should receive
429 Too Many Requests.
- MongoDB apps may be vulnerable to NoSQL injection via operators like
$gtand$ne. - Inputs may contain XSS payloads.
- HTTP Parameter Pollution can cause unexpected behavior when query params are repeated.
Three global protection layers were added before routes:
express-mongo-sanitizeto mitigate NoSQL injectionexpress-xss-sanitizerto sanitize inputs against XSShppto protect against HTTP Parameter Pollution
src/app.jspackage.json
- Send a body containing
{ "email": { "$gt": "" } }to login → it should not behave as an injected query. - Try repeated query params intentionally → their impact should be reduced.
- There was a CORS option mistake: using
credentialinstead ofcredentials. - Using
*withcredentials: trueis practically incorrect and insecure. - A whitelist was needed to control which browser origins are allowed.
- A whitelist was built from
CORS_ORIGINas a comma-separated list of allowed origins. credentials: truewas correctly configured.- Requests without an Origin (Postman/curl) were allowed to support development.
- Preflight handling was fixed without relying on
"*"which can cause issues in some environments.
src/app.js.env.example(and/or environment configuration)
- From a non-whitelisted origin → the browser should block the request (CORS error).
- From an allowed origin → the request should succeed.
- Postman/curl → usually works because there is no Origin header.
- Returning
accessToken/refreshTokeninside JSON responses increases the chance of leakage through XSS, logs, or debugging tools. - Cookie settings were incomplete (sameSite/maxAge) or configured in a way that could break development.
- Authentication was changed to rely on HttpOnly cookies only.
- Cookie options were hardened:
httpOnly: truesecuredepending on environment (Production vs Development)sameSitedepending on the scenariomaxAgederived from token expiry
- Tokens were no longer returned in JSON.
verifyJWTwas updated to read the token from cookies (with optional support for Authorization header).
src/controllers/user.controller.jssrc/middlewares/auth.middleware.jssrc/utils/cookies.js(or a similar helper file)package.json
- Login → should return user/success message without tokens in JSON, and cookies should be set.
- Any protected route → should work using cookies.
- Storing refresh tokens in plaintext in the DB is a major risk (if DB leaks, sessions are compromised).
- Without rotation, the same refresh token can be reused for a long time.
- Without reuse detection, a stolen token can continue to work.
- Refresh tokens are stored as a hash in the DB rather than the raw token.
- On every refresh:
- A new refresh token is issued (Rotation).
- The stored hash is updated.
- Revoke on reuse:
- If an incoming refresh token does not match the stored hash, it is treated as a reuse attempt and the session is revoked (hash removed) and the user must login again.
src/models/user.model.js(replacingrefreshTokenwithrefreshTokenHash)src/controllers/user.controller.jssrc/middlewares/auth.middleware.js
- Login → receive refresh cookie.
- Refresh once → should issue a new refresh cookie (rotation).
- Try using the old refresh token → should return 401 and revoke the session.
- Using
file.originalnamecan lead to:- overwrite issues
- unsafe file names
- Uploading inside
publiccan expose uploaded files directly via static hosting. - Missing limits/fileFilter allows large or dangerous uploads.
- Files are stored outside
public(e.g.,tmp/uploads). - Filenames are generated randomly (UUID/crypto) with a controlled extension.
limitswere added for file size and count.fileFilterwas added to allow specific types only (images: jpg/png/webp).- Upload directory is created automatically if it does not exist.
src/middlewares/multer.middleware.js
- Upload a valid image → succeeds.
- Upload a pdf/exe → rejected.
- Upload a file larger than the limit → rejected (returned as 400 with the error handler).
- Without a 404 handler, responses are not consistent.
- Without a centralized error handler, it can result in:
- stack trace leakage
- inconsistent responses
- excessive repetitive try/catch blocks
- Added a
notFoundmiddleware for routes that do not exist. - Added a centralized
errorHandlerthat:- returns a consistent JSON response
- hides stack traces in production
- handles common errors (Multer/Mongoose) with appropriate responses
src/middlewares/error.middleware.jssrc/app.js
- Call a non-existing route → 404 with a clear message.
- Trigger an error intentionally (e.g., upload a large file) → 400/500 in a structured response.
- Logging
req.bodyorreq.filesusingconsole.logcan leak:- passwords
- tokens
- cookies
- other sensitive data
console.logis not production-grade and does not support log levels or structured output.
- Removed any logs that printed sensitive contents.
- Added a professional logger (e.g., Pino) with:
- log levels (info/warn/error)
- request logging
- redaction to remove/hide sensitive paths (authorization/cookies/password/token)
- Integrated logger with the error handler to record errors safely.
src/utils/logger.jssrc/middlewares/logger.middleware.jssrc/controllers/*(removing sensitive console.log)src/db/*andsrc/index.js(replacing console.log with logger)src/middlewares/error.middleware.jspackage.json
- Perform login with a password and verify that logs do not show password/token/cookie values.
- Server startup sequencing: DB before listen to prevent runtime failures.
- Helmet and secure HTTP headers to reduce attack surface.
- Rate limiting to protect authentication endpoints from brute force and DoS.
- The difference between NoSQL injection, XSS, and HPP and how each is mitigated.
- Correct CORS configuration with credentials and why
*cannot be used with it. - Cookie security flags:
httpOnly,secure,sameSite,maxAge, and how they differ by environment. - Refresh token security: hashing, rotation, reuse detection, and revocation.
- Upload security: fileFilter/limits and avoiding public upload storage.
- Centralized Express error handling without leaking sensitive information.
- Production-grade logging with redaction to prevent secrets leakage and improve observability.
.envmust not be pushed to GitHub; only.env.exampleshould be committed.- If the frontend is hosted on a different domain in production, cookies often require
sameSite: "none"withsecure: true, and CORS whitelist must be configured correctly.