📑 Table of Contents
Security is an unavoidable, critical topic when developing web applications. OWASP (Open Web Application Security Project) periodically publishes a "Top 10" list that systematically categorizes web security risks. This article explains the most dangerous threats and their specific defenses with real code examples.
The code examples in this article are for educational purposes. Using them for actual attacks is prohibited by law. Apply security knowledge for defense only.
1. What is OWASP & the Top 10
OWASP is a nonprofit organization dedicated to improving web application security. The OWASP Top 10 summarizes the most critical web security risks in 10 categories — a de facto standard referenced by developers and security engineers worldwide.
2025 OWASP Top 10 Overview
- A01: Broken Access Control — Missing authorization checks
- A02: Cryptographic Failures — Improper data encryption
- A03: Injection — SQL/NoSQL/OS command injection
- A04: Insecure Design — Architecture-level vulnerabilities
- A05: Security Misconfiguration — Default settings left unchanged
- A06: Vulnerable Components — Outdated libraries
- A07: Authentication Failures — Password and auth flow issues
- A08: Software & Data Integrity — CI/CD and update vulnerabilities
- A09: Logging & Monitoring Failures — Insufficient logging
- A10: SSRF — Server-Side Request Forgery
2. Injection Attacks
One of the oldest and most dangerous attack vectors is SQL Injection. Embedding user input directly into SQL queries allows attackers to manipulate your database.
❌ Vulnerable Code
// DANGER! Directly concatenating user input into SQL
app.post('/login', (req, res) => {
const { username, password } = req.body;
// If attacker enters ' OR '1'='1' -- as username...
const query = `SELECT * FROM users
WHERE username = '${username}'
AND password = '${password}'`;
db.query(query); // All user data leaked!
});
✅ Safe Code (Parameterized Queries)
// Use prepared statements
app.post('/login', (req, res) => {
const { username, password } = req.body;
const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
db.query(query, [username, password]); // Safe!
});
Using an ORM significantly reduces SQL injection risk. Actively leverage ORMs like Prisma, Sequelize, or TypeORM.
3. Broken Authentication
Authentication is the cornerstone of most applications. Let's look at common vulnerabilities and defenses.
Common Auth Issues
- Weak password policies: Allowing "password123"
- No brute force protection: Unlimited login attempts
- Session mismanagement: Predictable session IDs
- JWT misuse: Allowing alg=none, leaked secret keys
Secure Auth Implementation
import bcrypt from 'bcrypt';
import rateLimit from 'express-rate-limit';
// Rate limit login attempts
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5,
message: 'Too many login attempts. Try again in 15 minutes.'
});
app.post('/login', loginLimiter, async (req, res) => {
const { email, password } = req.body;
const user = await User.findByEmail(email);
if (!user) {
// Timing attack prevention
await bcrypt.compare(password, '$2b$10$invalidhashplaceholder');
return res.status(401).json({ error: 'Authentication failed' });
}
const isValid = await bcrypt.compare(password, user.passwordHash);
if (!isValid) {
return res.status(401).json({ error: 'Authentication failed' });
}
req.session.userId = user.id;
req.session.cookie.httpOnly = true;
req.session.cookie.secure = true;
req.session.cookie.sameSite = 'strict';
});
4. Cross-Site Scripting (XSS)
XSS attacks inject malicious scripts into web pages that execute in other users' browsers.
Three Types of XSS
- Stored XSS: Malicious script saved on the server (forums, comments)
- Reflected XSS: Occurs on pages that reflect URL parameters
- DOM-based XSS: Occurs when JavaScript dynamically manipulates the DOM
XSS Prevention
// ❌ Dangerous: inserting user input via innerHTML
element.innerHTML = userInput;
// ✅ Safe: use textContent
element.textContent = userInput;
// ✅ Server-side HTML escaping
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// ✅ Set Content Security Policy (CSP) header
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");
next();
});
5. CSRF Attacks & Defenses
CSRF (Cross-Site Request Forgery) tricks authenticated users into sending unintended requests using their active session.
import csrf from 'csurf';
const csrfProtection = csrf({ cookie: true });
app.get('/transfer', csrfProtection, (req, res) => {
res.render('transfer', { csrfToken: req.csrfToken() });
});
app.post('/transfer', csrfProtection, (req, res) => {
// Only executes if CSRF token is valid
processTransfer(req.body);
});
6. Security Checklist
Before releasing your web application, verify the following:
- ☑️ All user inputs are validated and sanitized
- ☑️ Parameterized queries are used (SQL injection prevention)
- ☑️ Passwords are hashed with bcrypt/argon2
- ☑️ HTTPS is enforced
- ☑️ CSP headers are configured
- ☑️ CSRF tokens are implemented
- ☑️ Session cookies have HttpOnly, Secure, and SameSite flags
- ☑️ Dependency vulnerabilities are regularly audited (
npm audit) - ☑️ Error messages don't leak sensitive information
- ☑️ Unauthorized access attempts are logged
Security is never "one and done." New vulnerabilities are constantly discovered, making continuous learning and updates essential. Start with the basics covered in this article and gradually deepen your security knowledge.