# Web Security Best Practices: From OWASP to Real-World Protection
Master XSS, CSRF, SQL injection prevention, authentication, HTTPS, and security headers
Web security is the foundation of modern application development. This article explores common threats and protection strategies.
OWASP Top 10 Overview
OWASP Top 10 2021:
┌─────────────────────────────────────────────────────┐
│ A01 - Broken Access Control │
│ A02 - Cryptographic Failures │
│ A03 - Injection │
│ A04 - Insecure Design │
│ A05 - Security Misconfiguration │
│ A06 - Vulnerable and Outdated Components │
│ A07 - Identification and Authentication Failures │
│ A08 - Software and Data Integrity Failures │
│ A09 - Security Logging and Monitoring Failures │
│ A10 - Server-Side Request Forgery (SSRF) │
└─────────────────────────────────────────────────────┘
XSS Cross-Site Scripting
Attack Types
| Type | Description | Protection |
|---|---|---|
| Stored XSS | Scripts stored in database | Output encoding |
| Reflected XSS | Scripts in URL parameters | Input validation |
| DOM-based XSS | Executed via DOM manipulation | CSP policy |
Output Encoding
// HTML escape function
function escapeHtml(unsafe: string): string {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// Usage example
const userInput = '<script>alert("xss")</script>';
const safeOutput = escapeHtml(userInput);
// Result: <script>alert("xss")</script>
CSP Content Security Policy
// Using helmet in Express.js
import helmet from 'helmet';
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
frameAncestors: ["'none'"],
},
})
);
CSRF Protection
Token Validation
import csrf from 'csurf';
// Enable CSRF protection
app.use(csrf({ cookie: true }));
// Generate Token
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// Use in form
// <input type="hidden" name="_csrf" value="{{ csrfToken }}" />
SameSite Cookie
res.cookie('session', sessionId, {
httpOnly: true, // Prevent JS access
secure: true, // HTTPS only
sameSite: 'strict', // Prevent CSRF
maxAge: 3600000,
});
SQL Injection Prevention
Parameterized Queries
// Dangerous: string concatenation
const unsafeQuery = `SELECT * FROM users WHERE id = ${userId}`; // Unsafe!
// Safe: parameterized queries
const result = await pool.query(
'SELECT * FROM users WHERE id = $1 AND status = $2',
[userId, 'active']
);
// Prisma ORM (automatic protection)
const user = await prisma.user.findFirst({
where: { email: userInput },
});
Input Validation
import { z } from 'zod';
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
age: z.number().min(0).max(150),
});
function validateInput(input: unknown) {
return UserSchema.parse(input);
}
Authentication & Authorization
JWT Security Practices
import jwt from 'jsonwebtoken';
// Generate Token
function generateToken(userId: string, roles: string[]) {
return jwt.sign(
{ sub: userId, roles },
process.env.JWT_SECRET!,
{ expiresIn: '15m' }
);
}
// Verify Token
function verifyToken(token: string) {
try {
return jwt.verify(token, process.env.JWT_SECRET!);
} catch {
return null;
}
}
// Authentication middleware
function authenticate(req, res, next) {
const token = req.headers.authorization?.slice(7);
const payload = verifyToken(token);
if (!payload) {
return res.status(401).json({ error: 'Unauthorized' });
}
req.user = payload;
next();
}
RBAC Access Control
type Role = 'user' | 'editor' | 'admin';
const permissions = {
user: ['read'],
editor: ['read', 'write'],
admin: ['read', 'write', 'delete'],
};
function authorize(requiredPermission: string) {
return (req, res, next) => {
const userRole = req.user?.role;
if (!permissions[userRole]?.includes(requiredPermission)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
Password Security
import bcrypt from 'bcrypt';
// Password hashing
async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 12);
}
// Password verification
async function verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
// Password strength validation
const passwordSchema = z.string()
.min(8, 'At least 8 characters')
.regex(/[A-Z]/, 'Requires uppercase')
.regex(/[a-z]/, 'Requires lowercase')
.regex(/[0-9]/, 'Requires number')
.regex(/[^A-Za-z0-9]/, 'Requires special character');
HTTPS and Security Headers
import helmet from 'helmet';
// Enable security headers
app.use(helmet());
// HSTS configuration
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true,
}));
// Force HTTPS
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(301, `https://${req.hostname}${req.url}`);
}
next();
});
Rate Limiting
import rateLimit from 'express-rate-limit';
// General limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
message: { error: 'Too many requests' },
});
// Login limiting (stricter)
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: { error: 'Too many login attempts' },
});
app.use('/api/', limiter);
app.post('/auth/login', loginLimiter);
Security Logging
function logSecurityEvent(event: {
type: string;
userId?: string;
ip: string;
details: Record<string, any>;
}) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
...event,
}));
}
// Log login attempts
logSecurityEvent({
type: 'LOGIN_ATTEMPT',
ip: req.ip,
details: { email, success: true },
});
Best Practices Summary
Web Security Checklist:
┌─────────────────────────────────────────────────────┐
│ Input Validation │
│ ├── Validate all inputs server-side │
│ ├── Use parameterized queries │
│ └── Sanitize HTML content │
│ │
│ Authentication Security │
│ ├── Strong password policies │
│ ├── Secure session management │
│ └── Token expiration │
│ │
│ Transport Security │
│ ├── Force HTTPS │
│ ├── Secure cookies │
│ └── Security headers │
│ │
│ Monitoring & Auditing │
│ ├── Log security events │
│ ├── Anomaly detection │
│ └── Regular security audits │
└─────────────────────────────────────────────────────┘
| Threat | Protection |
|---|---|
| XSS | CSP + Output Encoding |
| CSRF | Token + SameSite |
| SQL Injection | Parameterized Queries |
| Brute Force | Rate Limiting |
Security is not a feature, it’s quality. Embed security into every step of development.