Applying OWASP Security
Quick Start
import { z } from "zod";
import DOMPurify from "isomorphic-dompurify";
export const userSchema = z.object({
email: z.string().email().max(254),
password: z.string().min(12).max(128),
name: z.string().min(2).max(100).regex(/^[\p{L}\s'-]+$/u),
});
export const sanitizeHtml = (dirty: string) =>
DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p"] });
Features
| Feature |
Description |
Reference |
| Injection Prevention |
SQL, NoSQL, command injection protection |
OWASP Injection |
| XSS Prevention |
Output encoding and HTML sanitization |
OWASP XSS |
| CSRF Protection |
Token-based cross-site request forgery defense |
OWASP CSRF |
| Authentication Security |
Password hashing, rate limiting, session management |
OWASP Auth |
| Security Headers |
CSP, HSTS, X-Frame-Options configuration |
OWASP Headers |
| Input Validation |
Schema validation and sanitization |
OWASP Validation |
Common Patterns
Parameterized Queries (SQL Injection Prevention)
const result = await db.$queryRawUnsafe(`SELECT * FROM users WHERE id = '${userId}'`);
const result = await db.user.findUnique({ where: { id: userId } });
const result = await db.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
CSRF Protection Middleware
import crypto from "crypto";
export function csrfProtection(req: Request, res: Response, next: NextFunction) {
if (["GET", "HEAD", "OPTIONS"].includes(req.method)) return next();
const cookieToken = req.cookies["csrf_token"];
const headerToken = req.headers["x-csrf-token"];
if (!cookieToken || !headerToken || cookieToken !== headerToken) {
return res.status(403).json({ error: "CSRF validation failed" });
}
next();
}
Security Headers Configuration
import helmet from "helmet";
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'strict-dynamic'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
frameSrc: ["'none'"],
objectSrc: ["'none'"],
},
},
strictTransportSecurity: { maxAge: 31536000, includeSubDomains: true, preload: true },
frameguard: { action: "deny" },
}));
Password Security
import bcrypt from "bcrypt";
const SALT_ROUNDS = 12;
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
export function validatePasswordStrength(password: string): string[] {
const errors: string[] = [];
if (password.length < 12) errors.push("Must be at least 12 characters");
if (!/[a-z]/.test(password)) errors.push("Must contain lowercase");
if (!/[A-Z]/.test(password)) errors.push("Must contain uppercase");
if (!/\d/.test(password)) errors.push("Must contain digit");
if (!/[!@#$%^&*]/.test(password)) errors.push("Must contain special character");
return errors;
}
Best Practices
| Do |
Avoid |
| Validate all input on the server side |
Trusting client-side validation alone |
| Use parameterized queries for all DB access |
String concatenation in queries |
| Set security headers on all responses |
Disabling security features for convenience |
| Implement rate limiting on sensitive endpoints |
Allowing unlimited attempts |
| Hash passwords with bcrypt (12+ rounds) |
Using weak/deprecated crypto algorithms |
| Log security events for monitoring |
Exposing detailed error messages to users |
| Keep dependencies updated |
Ignoring security warnings |
| Use HTTPS for all communications |
Hardcoding secrets in source code |
References