Security Best Practices
Essential security patterns for web development.
Instructions
- Environment Variables
✅ .env file (never commit)
DATABASE_URL=postgresql://... JWT_SECRET=your-secret-key API_KEY=xxx
✅ .gitignore
.env .env.local .env.*.local
// ✅ Access with validation const dbUrl = process.env.DATABASE_URL; if (!dbUrl) throw new Error('DATABASE_URL required');
- XSS Prevention
// ❌ Bad - direct HTML injection element.innerHTML = userInput;
// ✅ Good - use textContent element.textContent = userInput;
// ✅ Good - sanitize if HTML needed import DOMPurify from 'dompurify'; element.innerHTML = DOMPurify.sanitize(userInput);
- SQL Injection Prevention
// ❌ Bad - string concatenation
const query = SELECT * FROM users WHERE id = ${userId};
// ✅ Good - parameterized queries const user = await prisma.user.findUnique({ where: { id: userId } });
// ✅ Good - prepared statements const [rows] = await db.execute( 'SELECT * FROM users WHERE id = ?', [userId] );
- Authentication
// ✅ Password hashing import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 12); const isValid = await bcrypt.compare(password, hash);
// ✅ JWT with expiration const token = jwt.sign( { userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' } );
- Input Validation
import { z } from 'zod';
const UserSchema = z.object({ email: z.string().email(), password: z.string().min(8), age: z.number().min(18).max(120) });
// Validate before use const result = UserSchema.safeParse(input); if (!result.success) { throw new Error('Invalid input'); }
- HTTPS & Headers
// ✅ Security headers app.use(helmet());
// ✅ CORS configuration app.use(cors({ origin: ['https://yourdomain.com'], credentials: true }));
- Rate Limiting
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit per IP });
app.use('/api', limiter);
References
-
OWASP Top 10
-
Node.js Security Checklist