Security22 min read
Security Best Practices for Modern Web Applications
Comprehensive guide to implementing security-conscious development practices and rigorous QA testing.
Security is paramount in modern web development. This guide covers essential security practices, vulnerability prevention, and QA testing strategies.
Input Validation and Sanitization
typescript
// Server-side validation with Zod
import { z } from 'zod';
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(50),
age: z.number().int().min(18).max(100),
});
export async function POST(request: Request) {
try {
const body = await request.json();
const validatedData = userSchema.parse(body);
// Process validated data
} catch (error) {
return Response.json({ error: 'Invalid input' }, { status: 400 });
}
}Authentication and Authorization
typescript
// Secure session management
import { SignJWT, jwtVerify } from 'jose';
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
export async function createToken(payload: any) {
return await new SignJWT(payload)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('2h')
.sign(secret);
}
export async function verifyToken(token: string) {
try {
const { payload } = await jwtVerify(token, secret);
return payload;
} catch {
return null;
}
}XSS Prevention
typescript
// Sanitize user input
import DOMPurify from 'isomorphic-dompurify';
export function sanitizeHTML(html: string) {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
ALLOWED_ATTR: ['href'],
});
}
// React: Use dangerouslySetInnerHTML carefully
<div dangerouslySetInnerHTML={{ __html: sanitizeHTML(userContent) }} />CSRF Protection
typescript
// Generate CSRF token
import { randomBytes } from 'crypto';
export function generateCSRFToken() {
return randomBytes(32).toString('hex');
}
// Verify CSRF token
export function verifyCSRFToken(token: string, sessionToken: string) {
return token === sessionToken;
}SQL Injection Prevention
typescript
// Use parameterized queries
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Safe
const user = await prisma.user.findUnique({
where: { email: userEmail }, // Parameterized
});
// Never do this
// const query = `SELECT * FROM users WHERE email = '${email}'`;Security Headers
typescript
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-DNS-Prefetch-Control',
value: 'on',
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload',
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin',
},
],
},
];
},
};Environment Variables Security
typescript
// Never expose secrets in client code
// Use server-side only
// .env.local (never commit)
DATABASE_URL=...
JWT_SECRET=...
API_KEY=...
// Only expose public vars
NEXT_PUBLIC_API_URL=...QA Testing Strategies
Unit Testing
typescript
// __tests__/utils.test.ts
import { sanitizeInput } from '@/utils/security';
describe('sanitizeInput', () => {
it('should remove script tags', () => {
const input = '<script>alert("xss")</script>Hello';
expect(sanitizeInput(input)).toBe('Hello');
});
});Integration Testing
typescript
// __tests__/api/auth.test.ts
import { POST } from '@/app/api/auth/route';
describe('POST /api/auth', () => {
it('should reject invalid credentials', async () => {
const response = await POST(new Request('http://localhost', {
method: 'POST',
body: JSON.stringify({ email: 'invalid', password: 'wrong' }),
}));
expect(response.status).toBe(401);
});
});Security Audits
bash
# Run security audits
npm audit
npm audit fix
# Use Snyk for vulnerability scanning
npx snyk testContent Security Policy
typescript
// Implement CSP
const cspHeader = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data: https:;
font-src 'self';
`;
// Add to headers
{
key: 'Content-Security-Policy',
value: cspHeader.replace(/s{2,}/g, ' ').trim(),
}Build secure applications with confidence using these practices.