Security Best Practices
Security isn’t optional—it’s a fundamental requirement for any production application. This chapter covers essential security practices every Node.js developer must implement.OWASP Top 10 for Node.js
| Vulnerability | Description | Prevention |
|---|---|---|
| Injection | SQL, NoSQL, Command injection | Input validation, parameterized queries |
| Broken Auth | Weak passwords, session hijacking | Strong auth, JWT best practices |
| Sensitive Data Exposure | Unencrypted data | HTTPS, encryption at rest |
| XXE | XML External Entities | Disable XML parsing or use safe parsers |
| Broken Access Control | Privilege escalation | RBAC, resource ownership validation |
| Security Misconfiguration | Default credentials, verbose errors | Secure defaults, minimal information |
| XSS | Cross-Site Scripting | Output encoding, CSP headers |
| Insecure Deserialization | Object manipulation | Input validation, schema enforcement |
| Vulnerable Components | Outdated dependencies | Regular updates, npm audit |
| Insufficient Logging | No audit trail | Comprehensive logging and monitoring |
Input Validation and Sanitization
Never trust user input. This is the single most important rule in application security. Every piece of data that crosses your API boundary — request bodies, query parameters, URL params, headers, cookies — should be treated as potentially hostile until proven otherwise. Think of it like airport security: every bag gets scanned, no exceptions, regardless of who is carrying it. Validation checks that the data has the expected shape and values (is this a valid email? is this number within range?). Sanitization transforms data to remove dangerous content (stripping HTML tags, escaping special characters).SQL Injection Prevention
SQL injection is one of the oldest and most devastating attacks in web security. It works by tricking your application into treating user-supplied data as SQL code. If a user submits'; DROP TABLE users; -- as their userId, and you concatenate it directly into a query string, the database executes the destructive command. Real-world SQL injection attacks have exposed millions of user records and caused data breaches at major companies.
NoSQL Injection Prevention
NoSQL injection is subtler than SQL injection but equally dangerous. Instead of injecting SQL syntax, attackers inject MongoDB query operators. For example, ifreq.body.password is { "$gt": "" } instead of a string, the query User.findOne({ password: req.body.password }) becomes User.findOne({ password: { $gt: "" } }) — which matches every user with a non-empty password. The attacker logs in without knowing any password.
This attack works because Express’s json() parser converts JSON objects in the request body into actual JavaScript objects, and Mongoose happily passes those objects to MongoDB as query operators.
Authentication Security
Security Headers with Helmet
HTTP security headers tell browsers how to behave when handling your site’s content. They are like instructions you give to the browser: “do not let other sites embed me in an iframe,” “only load scripts from my domain,” “always use HTTPS.” Without these headers, browsers use their permissive defaults, leaving your users vulnerable to cross-site scripting, clickjacking, and man-in-the-middle attacks. Helmet is a middleware that sets these headers for you with sensible defaults. One line of code, and you get protection that would otherwise require configuring a dozen headers manually.CORS Configuration
CORS (Cross-Origin Resource Sharing) is one of the most misunderstood concepts in web security. Here is the mental model: browsers enforce a rule that scripts onexample.com cannot make requests to api.otherdomain.com unless api.otherdomain.com explicitly says “I allow requests from example.com.” This prevents malicious sites from using your users’ cookies to call your API on their behalf.
CORS is enforced by the browser, not the server. Your server only sets headers that tell the browser what is allowed. Tools like curl or Postman ignore CORS entirely — it is purely a browser-based security mechanism.
Environment Variables Security
Environment variables are the standard way to pass secrets (API keys, database passwords, JWT secrets) to your application without hardcoding them in source code. The principle is simple: secrets belong to the environment, not to the codebase. Your code should read them at runtime, and they should never appear in your git history. If a secret is committed to git even once, consider it compromised — git history is permanent and commonly cloned by many developers. Rotate the secret immediately.Dependency Security
File Upload Security
File uploads are one of the most dangerous features you can add to a web application. Without proper validation, an attacker can upload executable scripts, overwrite system files via path traversal (../../etc/passwd), or exhaust disk space with oversized files. Every file upload must be validated for type, size, and content — and the original filename should never be trusted or used directly.
Secure Cookie Configuration
Cookies are the primary target for session hijacking attacks. A poorly configured cookie can be stolen via cross-site scripting (XSS), sent over unencrypted HTTP, or exploited via cross-site request forgery (CSRF). Each cookie flag below closes one of these attack vectors.Logging Security Events
Security Checklist
Summary
- Validate all input - Never trust client data
- Prevent injection - Use parameterized queries
- Secure authentication - Hash passwords, rate limit, lockout
- Set security headers - Use Helmet
- Configure CORS properly - Whitelist origins
- Keep dependencies updated - Run npm audit regularly
- Handle files securely - Validate type, size, scan for malware
- Log security events - Audit trail for incidents
- Use HTTPS everywhere - Encrypt data in transit
- Follow the principle of least privilege - Minimal permissions