Implementing Security Best Practices

1. Installing helmet Package

Example: Install

npm i helmet express-rate-limit hpp express-mongo-sanitize

2. Using helmet Middleware

Example: Default helmet

import helmet from "helmet";
app.use(helmet());
Header set by helmetPurpose
Content-Security-PolicyMitigate XSS / data injection
Strict-Transport-SecurityForce HTTPS
X-Content-Type-OptionsDisable MIME sniffing
X-Frame-OptionsClickjacking
Referrer-PolicyControl Referer
Cross-Origin-*-PolicyCOOP / COEP / CORP

3. Preventing XSS Attacks

Example: Escape in templates + CSP

// Templates auto-escape by default (EJS <%= %>, Pug #{}, Handlebars {{}})
// For raw strings to client, use res.json (auto-escapes JSON)
// For HTML output, use a sanitizer:
import sanitizeHtml from "sanitize-html";
const safe = sanitizeHtml(userInput, { allowedTags: ["b","i","a"], allowedAttributes: { a: ["href"] } });

4. Implementing Rate Limiting

Example: Quick limiter

import rateLimit from "express-rate-limit";
app.use(rateLimit({ windowMs: 15 * 60_000, limit: 1000, standardHeaders: "draft-7" }));

5. Preventing SQL Injection

Example: Parameterized query

// SAFE — placeholders
await pool.query("SELECT * FROM users WHERE email = $1", [email]);

// UNSAFE — string concat
await pool.query(`SELECT * FROM users WHERE email = '${email}'`);  // ⚠️ NEVER

6. Sanitizing User Input

Example: NoSQL injection guard

import mongoSanitize from "express-mongo-sanitize";
app.use(mongoSanitize());  // strips $ and . keys from req.body/query/params

7. Setting Secure Headers

Example: Custom CSP + HSTS

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc:  ["'self'","https://cdn.example.com"],
      imgSrc:     ["'self'","data:","https:"],
      connectSrc: ["'self'","https://api.example.com"],
      objectSrc:  ["'none'"],
      upgradeInsecureRequests: []
    }
  },
  strictTransportSecurity: { maxAge: 63072000, includeSubDomains: true, preload: true }
}));

8. Preventing CSRF Attacks

Example: csrf-csrf double-submit

import { doubleCsrf } from "csrf-csrf";

const { doubleCsrfProtection, generateToken } = doubleCsrf({
  getSecret: () => process.env.CSRF_SECRET,
  cookieName: "__Host-psifi.x-csrf-token",
  cookieOptions: { httpOnly: true, secure: true, sameSite: "lax" }
});

app.get("/csrf", (req, res) => res.json({ token: generateToken(req, res) }));
app.use(doubleCsrfProtection);
Note: csurf is deprecated. Use csrf-csrf or rely on SameSite=Lax cookies + custom header for SPAs.

9. Using HTTPS

Example: Trust proxy + redirect

app.set("trust proxy", 1);  // honor X-Forwarded-Proto from load balancer
app.use((req, res, next) => {
  if (!req.secure && process.env.NODE_ENV === "production") {
    return res.redirect(301, `https://${req.headers.host}${req.url}`);
  }
  next();
});

10. Securing Cookies

AttributeRecommendation
HttpOnlytrue (block JS access)
Securetrue (HTTPS only)
SameSitelax (default) or strict
PathNarrowest possible
Prefix__Host- for tightest binding

11. Implementing Input Validation

Example: zod at boundary

const createUser = z.object({
  email: z.string().email().max(254),
  age:   z.number().int().min(0).max(150)
});
app.post("/users", (req, res) => {
  const data = createUser.parse(req.body);  // throws ZodError → 400
  // ...
});

12. Preventing Parameter Pollution

Example: hpp middleware

import hpp from "hpp";
app.use(hpp({ whitelist: ["tag","sort"] }));  // collapses ?id=1&id=2 into id=2 except whitelisted