Introduction: Why JavaScript Application Security is Not an Option, But a Necessity
JavaScript (especially on the Node.js platform) has become the dominant language for building both client-side and server-side applications. However, with growing popularity comes an increase in the number of attacks. According to OWASP, most breaches occur due to common mistakes that can be prevented. In this article, we will cover the top 10 JavaScript (Node.js) vulnerabilities with real code examples and protection methods. This material will be useful for both beginners and experienced developers looking to strengthen the security of their projects.
1. Injections (SQL, NoSQL, Command Injection)
Injections remain the most dangerous category of vulnerabilities. In Node.js, MongoDB and PostgreSQL are often affected.
Example of vulnerable code (NoSQL injection):
// Vulnerable codeapp.post('/login', async (req, res) => { const { username, password } = req.body; const user = await db.collection('users').findOne({ username: username, password: password }); // If you pass { "username": { "$ne": null }, "password": { "$ne": null } }, the first user will be returned});How to fix:
- Use ORM/ODM with parameterization (Mongoose, Sequelize).
- Validate and sanitize input data (library
validatororjoi). - For commands, use
execFileinstead ofexec.
2. Cross-Site Scripting (XSS)
XSS allows an attacker to inject a malicious script into a page viewed by other users.
Example of vulnerable code (Reflected XSS):
// Vulnerable Express routeapp.get('/search', (req, res) => { const query = req.query.q; res.send(`<h1>Search results for: ${query}</h1>`); // If you pass ?q=<script>alert('XSS')</script>, the script will execute});Protection:
- Escape output: use
res.renderwith template engines (EJS, Pug) orencodeURIComponent. - Set the HTTP header
Content-Security-Policy. - Do not use
innerHTMLon the client — usetextContent.
3. Insecure Authentication and Session Management
Weak authentication is one of the main causes of data leaks. Developers often hardcode keys or use weak passwords.
Common mistakes:
- Storing passwords in plain text (instead of bcrypt/argon2).
- Using predictable JWT secrets (
secret123). - Lack of rate limiting on login attempts.
Example of correct implementation:
const bcrypt = require('bcrypt');const saltRounds = 12;
app.post('/register', async (req, res) => { const hash = await bcrypt.hash(req.body.password, saltRounds); // Save hash to DB});
app.post('/login', async (req, res) => { const user = await db.getUser(req.body.username); const match = await bcrypt.compare(req.body.password, user.passwordHash); if (match) { // Create JWT with a short expiration time const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: '15m' }); res.json({ token }); }});4. SSRF (Server-Side Request Forgery)
The SSRF vulnerability allows an attacker to force the server to make requests to internal resources (e.g., cloud provider metadata).
Example:
// Vulnerable code: loading an avatar by URLapp.post('/avatar', async (req, res) => { const { url } = req.body; const response = await fetch(url); // Can pass http://169.254.169.254/latest/meta-data/ // Process response});Protection:
- Create a whitelist of allowed domains.
- Block