Back to Blog
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 test

Content 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.