Implementing Security Best Practices
1. Installing helmet Package
2. Using helmet Middleware
| Header set by helmet | Purpose |
|---|---|
| Content-Security-Policy | Mitigate XSS / data injection |
| Strict-Transport-Security | Force HTTPS |
| X-Content-Type-Options | Disable MIME sniffing |
| X-Frame-Options | Clickjacking |
| Referrer-Policy | Control Referer |
| Cross-Origin-*-Policy | COOP / 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
| Attribute | Recommendation |
|---|---|
| HttpOnly | true (block JS access) |
| Secure | true (HTTPS only) |
| SameSite | lax (default) or strict |
| Path | Narrowest 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
// ...
});