JavaScript Security Best Practices: Protect Your Code from Attacks
JavaScript powers most modern web applications, making it a prime target for attackers. Following security best practices is essential to protect your users and your application from vulnerabilities.
1. Always Validate and Sanitize User Input
Never trust user input. All data coming from users, APIs, or external sources should be validated and sanitized.
Bad Practice:
// ❌ Dangerous - No validation
const username = req.body.username;
document.getElementById('welcome').innerHTML = 'Hello ' + username;Good Practice:
// ✅ Safe - Validate and escape
const username = sanitize(req.body.username);
document.getElementById('welcome').textContent = 'Hello ' + username;2. Prevent Cross-Site Scripting (XSS)
XSS is one of the most common JavaScript vulnerabilities. Attackers inject malicious scripts that run in users' browsers.
Prevention techniques:
- Use
textContentinstead ofinnerHTMLwhen displaying user data - Sanitize HTML with libraries like DOMPurify
- Implement Content Security Policy (CSP) headers
- Escape special characters in user input
Example:
// ❌ Vulnerable to XSS
element.innerHTML = userInput;
// ✅ Safe from XSS
element.textContent = userInput;
// ✅ Safe HTML rendering with DOMPurify
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);3. Use Strict Mode
Always enable strict mode to catch common coding errors and prevent unsafe actions.
'use strict';
// This will now throw an error instead of silently failing
undeclaredVariable = 10; // ReferenceError4. Secure Authentication and Session Management
Authentication vulnerabilities can lead to account takeovers and data breaches.
Best practices:
- Store tokens in httpOnly cookies (not localStorage)
- Implement proper session timeout
- Use secure, random session IDs
- Enable HTTPS for all authentication requests
- Implement rate limiting on login endpoints
// ❌ Insecure - Token in localStorage
localStorage.setItem('token', authToken);
// ✅ Secure - httpOnly cookie
res.cookie('token', authToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000
});5. Avoid eval() and Similar Functions
Never use eval(), Function(), or setTimeout() with strings. They can execute arbitrary code.
// ❌ Extremely dangerous
eval(userInput);
// ❌ Also dangerous
new Function(userInput)();
setTimeout(userInput, 1000);
// ✅ Use safe alternatives
const result = JSON.parse(userInput); // For data
const fn = myFunction; // Direct function reference6. Implement Content Security Policy (CSP)
CSP helps prevent XSS attacks by controlling which resources can be loaded.
// Set CSP headers in your server
Content-Security-Policy:
default-src 'self';
script-src 'self' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;7. Secure API Calls
When making API requests, implement proper security measures.
- Use HTTPS for all API calls
- Validate API responses
- Implement rate limiting
- Don't expose sensitive data in URLs
- Use proper CORS configuration
// ❌ Bad - Sensitive data in URL
fetch('https://api.example.com/user?token=' + userToken);
// ✅ Good - Token in header
fetch('https://api.example.com/user', {
headers: {
'Authorization': 'Bearer ' + userToken,
'Content-Type': 'application/json'
}
});8. Prevent Prototype Pollution
Prototype pollution can allow attackers to modify JavaScript object prototypes.
// ❌ Vulnerable to prototype pollution
function merge(target, source) {
for (let key in source) {
target[key] = source[key];
}
}
// ✅ Safe - Check for __proto__
function safeMerge(target, source) {
for (let key in source) {
if (key === '__proto__' || key === 'constructor') continue;
target[key] = source[key];
}
}
// ✅ Even better - Use Object.assign
const merged = Object.assign({}, target, source);9. Use Security Linters
Automated tools can catch security issues during development.
- ESLint Security Plugin: Detects common security issues
- npm audit: Finds vulnerabilities in dependencies
- ProbCheck: AI-powered vulnerability scanner
10. Keep Dependencies Updated
Outdated packages are a major security risk. Regularly update your dependencies.
# Check for vulnerabilities
npm audit
# Update packages
npm update
# Check for outdated packages
npm outdated11. Secure File Uploads
File uploads can be exploited to upload malicious scripts.
- Validate file types on the server
- Limit file size
- Rename uploaded files
- Store files outside web root
- Scan files for malware
12. Implement Rate Limiting
Prevent brute force attacks and DDoS with rate limiting.
// Using express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);Quick Security Checklist
- ✅ All user input validated and sanitized
- ✅ Using textContent instead of innerHTML
- ✅ CSP headers implemented
- ✅ HTTPS enabled everywhere
- ✅ Tokens in httpOnly cookies
- ✅ Dependencies up to date
- ✅ Rate limiting on sensitive endpoints
- ✅ Error messages don't leak sensitive info
- ✅ Security linters configured
- ✅ Regular security audits
Conclusion
JavaScript security requires constant vigilance. By following these best practices, you can significantly reduce your application's attack surface and protect your users' data.
Remember: Security is not a one-time effort. Regularly scan your code, update dependencies, and stay informed about new vulnerabilities.
Scan Your JavaScript Code for Free
ProbCheck automatically detects XSS, injection vulnerabilities, and 40+ other security issues in your JavaScript code. Get instant security insights.
Start Free Scan →