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 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
Example: NoSQL injection guard
import mongoSanitize from "express-mongo-sanitize" ;
app. use ( mongoSanitize ()); // strips $ and . keys from req.body/query/params
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
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