\", script executes!\n```\n\n### The Solution\n\n```typescript\n// ✅ SAFE - React escapes by default\nfunction UserGreeting({ name }: { name: string }) {\n return
Hello {name}
;\n}\n\n// ✅ If you must use HTML, sanitize it\nimport DOMPurify from 'dompurify';\n\nfunction SafeHTML({ content }: { content: string }) {\n const clean = DOMPurify.sanitize(content);\n return
;\n}\n```\n\n## 2. Cross-Site Request Forgery (CSRF)\n\nCSRF tricks users into performing actions they didn't intend.\n\n### Protection with CSRF Tokens\n\n```typescript\n// Server-side: Generate CSRF token\napp.get('/form', (req, res) => {\n const csrfToken = generateCSRFToken();\n req.session.csrfToken = csrfToken;\n res.render('form', { csrfToken });\n});\n\n// Verify on POST\napp.post('/action', (req, res) => {\n if (req.body.csrfToken !== req.session.csrfToken) {\n return res.status(403).send('Invalid CSRF token');\n }\n // Process request\n});\n```\n\n```typescript\n// Client-side: Include token in forms\n
\n \n \n
\n```\n\n## 3. Authentication & Authorization\n\n### Secure Password Storage\n\n```typescript\nimport bcrypt from 'bcrypt';\n\n// ❌ NEVER store plain text passwords\nconst password = 'user123';\n\n// ✅ Hash passwords with bcrypt\nasync function hashPassword(password: string) {\n const saltRounds = 12;\n return await bcrypt.hash(password, saltRounds);\n}\n\n// Verify password\nasync function verifyPassword(password: string, hash: string) {\n return await bcrypt.compare(password, hash);\n}\n```\n\n### JWT Authentication\n\n```typescript\nimport jwt from 'jsonwebtoken';\n\n// Generate token\nfunction generateToken(userId: string) {\n return jwt.sign(\n { userId },\n process.env.JWT_SECRET!,\n { expiresIn: '1h' }\n );\n}\n\n// Verify token middleware\nfunction authenticateToken(req, res, next) {\n const token = req.headers['authorization']?.split(' ')[1];\n \n if (!token) {\n return res.status(401).json({ error: 'No token provided' });\n }\n\n jwt.verify(token, process.env.JWT_SECRET!, (err, decoded) => {\n if (err) {\n return res.status(403).json({ error: 'Invalid token' });\n }\n req.userId = decoded.userId;\n next();\n });\n}\n```\n\n## 4. SQL Injection Prevention\n\n### The Problem\n\n```typescript\n// ❌ VULNERABLE to SQL injection\nconst query = `SELECT * FROM users WHERE email = '${email}'`;\n// If email = \"'; DROP TABLE users; --\", disaster!\n```\n\n### The Solution\n\n```typescript\n// ✅ Use parameterized queries\nimport { db } from './database';\n\n// With Prisma\nconst user = await db.user.findUnique({\n where: { email: email }\n});\n\n// With raw SQL - use parameters\nconst user = await db.query(\n 'SELECT * FROM users WHERE email = $1',\n [email]\n);\n```\n\n## 5. Security Headers\n\nImplement security headers:\n\n```typescript\n// Next.js middleware\nexport function middleware(request: NextRequest) {\n const response = NextResponse.next();\n\n // Prevent clickjacking\n response.headers.set('X-Frame-Options', 'DENY');\n \n // Enable XSS protection\n response.headers.set('X-XSS-Protection', '1; mode=block');\n \n // Prevent MIME sniffing\n response.headers.set('X-Content-Type-Options', 'nosniff');\n \n // Enforce HTTPS\n response.headers.set(\n 'Strict-Transport-Security',\n 'max-age=31536000; includeSubDomains'\n );\n \n // Content Security Policy\n response.headers.set(\n 'Content-Security-Policy',\n \"default-src 'self'; script-src 'self' 'unsafe-inline';\"\n );\n\n return response;\n}\n```\n\n## 6. HTTPS Everywhere\n\n```typescript\n// Redirect HTTP to HTTPS\napp.use((req, res, next) => {\n if (req.protocol === 'http' && process.env.NODE_ENV === 'production') {\n return res.redirect(301, `https://${req.headers.host}${req.url}`);\n }\n next();\n});\n```\n\n## 7. Rate Limiting\n\nPrevent brute force attacks:\n\n```typescript\nimport rateLimit from 'express-rate-limit';\n\nconst limiter = rateLimit({\n windowMs: 15 * 60 * 1000, // 15 minutes\n max: 100, // Limit each IP to 100 requests per window\n message: 'Too many requests, please try again later.'\n});\n\napp.use('/api/', limiter);\n\n// Stricter limit for login\nconst loginLimiter = rateLimit({\n windowMs: 15 * 60 * 1000,\n max: 5,\n message: 'Too many login attempts'\n});\n\napp.post('/api/login', loginLimiter, loginHandler);\n```\n\n## 8. Input Validation\n\nAlways validate and sanitize input:\n\n```typescript\nimport { z } from 'zod';\n\n// Define schema\nconst userSchema = z.object({\n email: z.string().email(),\n password: z.string().min(8).max(100),\n age: z.number().int().min(13).max(150),\n});\n\n// Validate input\nfunction createUser(input: unknown) {\n try {\n const validated = userSchema.parse(input);\n // Safe to use validated data\n return createUserInDB(validated);\n } catch (error) {\n return { error: 'Invalid input' };\n }\n}\n```\n\n## 9. Environment Variables\n\nNever expose secrets:\n\n```typescript\n// ❌ NEVER commit this\nconst apiKey = 'sk_live_abc123xyz789';\n\n// ✅ Use environment variables\nconst apiKey = process.env.API_KEY;\n\n// ✅ Validate on startup\nif (!process.env.API_KEY) {\n throw new Error('API_KEY is required');\n}\n```\n\n```text\n# .env (add to .gitignore)\nAPI_KEY=your_secret_key\nDATABASE_URL=postgresql://...\nJWT_SECRET=random_secret_string\n```\n\n## 10. Dependency Security\n\nKeep dependencies updated and check for vulnerabilities:\n\n```bash\n# Check for vulnerabilities\nnpm audit\n\n# Fix vulnerabilities\nnpm audit fix\n\n# Use tools like Snyk\nnpx snyk test\n```\n\n## Security Checklist\n\n- [ ] Sanitize all user input\n- [ ] Use HTTPS everywhere\n- [ ] Implement CSRF protection\n- [ ] Hash passwords with bcrypt\n- [ ] Use parameterized queries\n- [ ] Set security headers\n- [ ] Implement rate limiting\n- [ ] Validate environment variables\n- [ ] Keep dependencies updated\n- [ ] Regular security audits\n- [ ] Use Content Security Policy\n- [ ] Implement proper authentication\n- [ ] Log security events\n- [ ] Use secure session management\n\n## Conclusion\n\nSecurity is an ongoing process, not a one-time task. Stay updated on security best practices, audit your code regularly, and always think like an attacker.\n\nRemember: **Security is everyone's responsibility!** 🔒\n","url":"https://lwh.codes/blog/web-security-essentials","publisher":{"@type":"Person","name":"lwh"}}
5 min readlwh

Web Security Essentials: Protecting Your Applications

Essential web security practices every developer must know. Learn about XSS, CSRF, authentication, and how to build secure web applications.

Web Security Essentials: Protecting Your Applications

Loading...