Building Secure Web Applications: A Developer's Guide to Modern Security

Essential security practices for web developers, covering authentication, input validation, XSS prevention, and secure coding patterns.

Web application security is paramount in today's digital landscape. This comprehensive guide covers essential security practices every developer must implement to protect users and data.

Authentication and Authorization

Secure Password Handling

Never store passwords in plain text. Use proper hashing:

const bcrypt = require('bcrypt');

async function hashPassword(password) { const saltRounds = 12; return await bcrypt.hash(password, saltRounds); }

async function verifyPassword(password, hash) { return await bcrypt.compare(password, hash); }

JWT Best Practices

const jwt = require('jsonwebtoken');

// Generate token with expiration const token = jwt.sign( { userId: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '15m' } );

// Verify token middleware const verifyToken = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'No token provided' }); } try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (error) { res.status(403).json({ error: 'Invalid token' }); } };

Input Validation and Sanitization

Server-Side Validation

const joi = require('joi');

const userSchema = joi.object({ email: joi.string().email().required(), password: joi.string().min(8).pattern(new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\\$%\\^&\\*])')).required(), name: joi.string().min(2).max(50).required() });

app.post('/register', async (req, res) => { const { error, value } = userSchema.validate(req.body); if (error) { return res.status(400).json({ error: error.details[0].message }); } // Process validated data });

Cross-Site Scripting (XSS) Prevention

Content Security Policy

// Express.js with helmet
const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'", "https://apis.google.com"], styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"], fontSrc: ["'self'", "https://fonts.gstatic.com"], imgSrc: ["'self'", "data:", "https:"] } }));

Input Sanitization

const DOMPurify = require('isomorphic-dompurify');

function sanitizeHtml(dirty) { return DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['p', 'br', 'strong', 'em'], ALLOWED_ATTR: [] }); }

SQL Injection Prevention

Always use parameterized queries:

// ❌ Vulnerable
const query = SELECT * FROM users WHERE email = '${email}';

// ✅ Secure const query = 'SELECT * FROM users WHERE email = ?'; const user = db.prepare(query).get(email);

HTTPS and Security Headers

// Force HTTPS
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https') {
    res.redirect(https://${req.header('host')}${req.url});
  } else {
    next();
  }
});

// Security headers app.use(helmet({ hsts: { maxAge: 31536000, includeSubDomains: true, preload: true } }));

Rate Limiting

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests from this IP' });

app.use('/api/', limiter);

Security is an ongoing process. Regular security audits, dependency updates, and staying informed about new vulnerabilities are essential for maintaining secure applications.

← Back to Blog 📄 Print Article 🔗 Share