
Web Security: Protecting Your Application from Hackers
Web applications are always targets for attacks. A single vulnerability can bring down an entire system. This guide covers the most common web vulnerabilities and their defenses.
OWASP Top 10
OWASP (Open Web Application Security Project) publishes the top 10 most dangerous web vulnerabilities annually.
1. SQL Injection
Attack Principle: Occurs when user input is inserted directly into SQL queries without validation.
// Vulnerable code (NEVER use!)
app.get('/user', (req, res) => {
const userId = req.query.id;
const query = `SELECT * FROM users WHERE id = ${userId}`;
db.query(query, (err, results) => {
res.json(results);
});
});
// Attack examples
// GET /user?id=1 OR 1=1 → Leaks all user information
// GET /user?id=1; DROP TABLE users; → Deletes table
Defense Methods:
1) Prepared Statements
// Safe code
app.get('/user', (req, res) => {
const userId = req.query.id;
const query = 'SELECT * FROM users WHERE id = ?';
db.query(query, [userId], (err, results) => {
res.json(results);
});
});
2) Use ORM
// Sequelize (ORM)
const user = await User.findOne({
where: { id: userId }
});
// Automatic SQL Injection prevention
3) Input Validation
const validateUserId = (id) => {
const numericId = parseInt(id, 10);
if (isNaN(numericId) || numericId < 1) {
throw new Error("Invalid ID");
}
return numericId;
};
2. XSS (Cross-Site Scripting)
Attack Principle: Injects malicious JavaScript code into web pages for execution.
// Vulnerable code
app.get('/search', (req, res) => {
const keyword = req.query.q;
res.send(`<h1>Search results: ${keyword}</h1>`);
});
// Attack example
// GET /search?q=<script>alert(document.cookie)</script>
Defense Methods:
1) HTML Escaping
const escapeHtml = (unsafe) => {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};
2) Use Frameworks like React
// React automatically escapes
function SearchResults({ keyword }) {
return <h1>Search results: {keyword}</h1>;
// <script> tags displayed as text
}
3) Content Security Policy (CSP)
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' https://trusted-cdn.com"
);
next();
});
3. CSRF (Cross-Site Request Forgery)
Attack Principle: Forces users to execute unintended requests.
<!-- Malicious site -->
<img src="https://bank.com/transfer?to=hacker&amount=1000000">
<!-- Auto-executes if user is logged into bank.com -->
Defense Methods:
1) CSRF Tokens
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.post('/transfer', csrfProtection, (req, res) => {
processTransfer(req.body);
});
2) SameSite Cookies
res.cookie('sessionId', sessionId, {
httpOnly: true,
secure: true,
sameSite: 'strict' // Blocks cookie sending from other sites
});
4. Authentication and Session Management Vulnerabilities
Safe Methods:
1) Password Hashing
const bcrypt = require('bcrypt');
// Signup
app.post('/signup', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
await db.query(
'INSERT INTO users (username, password) VALUES (?, ?)',
[username, hashedPassword]
);
});
// Login
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await db.query(
'SELECT * FROM users WHERE username = ?',
[username]
);
const isValid = await bcrypt.compare(password, user.password);
if (isValid) {
req.session.userId = user.id;
res.json({ success: true });
}
});
2) Secure Session Management
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // Block JavaScript access
maxAge: 3600000, // Expire after 1 hour
sameSite: 'strict'
}
}));
5. Sensitive Data Exposure
Safe Methods:
1) Return Only Necessary Fields
app.get('/user/:id', async (req, res) => {
const user = await User.findById(req.params.id);
const safeUser = {
id: user.id,
username: user.username,
email: user.email,
// Exclude password, ssn, etc.
};
res.json(safeUser);
});
2) Manage Secrets with Environment Variables
require('dotenv').config();
const dbUrl = process.env.DATABASE_URL;
3) Use HTTPS
6. File Upload Vulnerabilities
Safe Methods:
const multer = require('multer');
const storage = multer.diskStorage({
destination: './uploads/',
filename: (req, file, cb) => {
const uniqueName = `${Date.now()}-${Math.random().toString(36)}${path.extname(file.originalname)}`;
cb(null, uniqueName);
}
});
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('File type not allowed'), false);
}
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: {
fileSize: 5 * 1024 * 1024 // 5MB limit
}
});
Security Headers
const helmet = require('helmet');
app.use(helmet()); // Automatic security header setup
Rate Limiting
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Max 100 requests
message: 'Too many requests. Please try again later.'
});
app.use('/api/', apiLimiter);
Input Validation
const validator = require('validator');
app.post('/user', (req, res) => {
const { email, username, age } = req.body;
if (!validator.isEmail(email)) {
return res.status(400).json({ error: 'Invalid email' });
}
if (!validator.isAlphanumeric(username)) {
return res.status(400).json({ error: 'Username must be alphanumeric' });
}
const cleanEmail = validator.normalizeEmail(email);
const cleanUsername = validator.escape(username);
saveUser({ email: cleanEmail, username: cleanUsername, age });
});
Security Checklist
Development Phase
- Validate and sanitize all inputs
- Prevent SQL Injection (Prepared Statements)
- Prevent XSS (HTML escaping)
- Use CSRF tokens
- Hash passwords (bcrypt, argon2)
- Use HTTPS
- Set security headers (Helmet.js)
- Apply Rate Limiting
- Validate file uploads
- Manage secrets with environment variables
Deployment Phase
- Check dependency vulnerabilities (
npm audit) - Apply latest security patches
- Configure firewall
- Monitor logs
- Regular backups
- Penetration testing
Web security is not a one-time task but an ongoing process.
Core Principles:
- Distrust all inputs
- Follow principle of least privilege
- Use defense in depth strategy
- Conduct regular security audits
A small mistake can lead to major damage. Security is not optional—it's essential.
