You might be using the latest React hooks and a scalable MongoDB cluster, but if your API endpoints are naked, a 15-year-old with a script can wipe your database in seconds.
Here are the 3 most common security holes in MERN apps — and the exact code to fix them.
1. The Silent Killer: NoSQL Injection
The Threat: Everyone talks about SQL Injection, but MERN apps use MongoDB (NoSQL). Developers assume they are safe. They are wrong.
If you pass user input directly to your database, a hacker can send a malicious command instead of a password.
The Vulnerable Code: A hacker sends { "$gt": "" } (greater than nothing) as the password. MongoDB interprets this as "Find any user where the password is greater than an empty string." Result: They log in as Admin without a password.
JavaScript
// DON'T DO THIS
app.post('/login', async (req, res) => {
// Passing req.body directly allows hackers to inject commands
const user = await User.findOne({
username: req.body.username,
password: req.body.password
});
if (user) { /* Logged in as Admin! */ }
});The Fix (Sanitization): Never trust user input. Use a library like mongo-sanitize to strip out keys that start with $ before they reach your database.
JavaScript
// DO THIS
const sanitize = require('mongo-sanitize');
app.post('/login', async (req, res) => {
// Clean the input first
const cleanUsername = sanitize(req.body.username);
const cleanPassword = sanitize(req.body.password);
const user = await User.findOne({
username: cleanUsername,
password: cleanPassword
});
});2. XSS (Cross-Site Scripting): React's Hidden Trap
The Threat: React is secure by default, but sometimes you need to render HTML from a user (like a blog post or comment).
If you force React to render raw HTML, a hacker can inject a script that steals every visitor's session cookies.
The Vulnerable Code: Using dangerouslySetInnerHTML is… well, dangerous.
JavaScript
// DON'T DO THIS
// If 'userContent' contains <script>alert('Hacked')</script>, it runs.
function BlogPost({ content }) {
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}The Fix (Purification): Use DOMPurify to scrub the HTML clean of malicious scripts before rendering it.
JavaScript
// DO THIS
import DOMPurify from 'dompurify';
function BlogPost({ content }) {
const cleanContent = DOMPurify.sanitize(content);
return <div dangerouslySetInnerHTML={{ __html: cleanContent }} />;
}3. The Invisible Shield: HTTP Headers
The Threat: By default, Express.js broadcasts "I am running Express" in the header of every response. This tells hackers exactly what vulnerabilities to look for.
The Fix (Helmet): You don't need to write complex logic here. Just use helmet. It automatically sets secure HTTP headers to hide your tech stack and prevent common attacks.
JavaScript:
// DO THIS
const helmet = require('helmet');
const express = require('express');
const app = express();
// Put this at the top of your middleware stack
app.use(helmet());Why It Matters (For Business Owners)
Security isn't just code; it's trust.
If your SaaS gets breached:
- You lose customers: 60% of small businesses close within 6 months of a cyber attack.
- You lose money: Recovering data costs 10x more than securing it.
- You lose sleep: Do you want to worry about your database every night?
Audit your server.js file today. If you don't see sanitize or helmet, you have work to do.