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.

⚠️ Important

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

  1. A01: Broken Access Control — Missing authorization checks
  2. A02: Cryptographic Failures — Improper data encryption
  3. A03: Injection — SQL/NoSQL/OS command injection
  4. A04: Insecure Design — Architecture-level vulnerabilities
  5. A05: Security Misconfiguration — Default settings left unchanged
  6. A06: Vulnerable Components — Outdated libraries
  7. A07: Authentication Failures — Password and auth flow issues
  8. A08: Software & Data Integrity — CI/CD and update vulnerabilities
  9. A09: Logging & Monitoring Failures — Insufficient logging
  10. 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!
});
✅ Defense Tip

Using an ORM significantly reduces SQL injection risk. Actively leverage ORMs like Prisma, Sequelize, or TypeORM.

Ad

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);
});
Ad

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.