Cybersecurity Essentials for Modern Web Applications
Web application security isn't optional—it's fundamental. A single breach can cost millions in damages, lost trust, and regulatory fines. This guide covers essential security practices every development team should implement.
OWASP Top 10 Vulnerabilities
1. Injection Attacks
SQL Injection Example:
// ❌ VULNERABLE
const query = `SELECT * FROM users WHERE email = '${email}'`
// ✅ SECURE - Use parameterized queries
const query = 'SELECT * FROM users WHERE email = ?'
const result = await db.query(query, [email])
Prevention:
- Use ORMs (Prisma, TypeORM)
- Parameterized queries
- Input validation
- Least-privilege database accounts
2. Broken Authentication
Secure Authentication:
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
// Hash passwords
const hashedPassword = await bcrypt.hash(password, 12)
// Verify password
const isValid = await bcrypt.compare(password, user.hashedPassword)
// Create JWT
const token = jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET!,
{ expiresIn: '1h' }
)
Best Practices: ✅ Multi-factor authentication (MFA) ✅ Password complexity requirements ✅ Account lockout after failed attempts ✅ Secure session management ✅ Password reset security
3. Cross-Site Scripting (XSS)
Prevention:
// ❌ VULNERABLE
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// ✅ SECURE - React automatically escapes
<div>{userInput}</div>
// For HTML content, sanitize first
import DOMPurify from 'dompurify'
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(userInput)
}} />
Content Security Policy:
// next.config.js
const ContentSecurityPolicy = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data:;
`
module.exports = {
async headers() {
return [{
source: '/:path*',
headers: [{
key: 'Content-Security-Policy',
value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim()
}]
}]
}
}
4. Cross-Site Request Forgery (CSRF)
CSRF Protection:
import { getCsrfToken } from 'next-auth/react'
// Generate token
const csrfToken = await getCsrfToken()
// Include in form
<form method="post" action="/api/update">
<input type="hidden" name="csrfToken" value={csrfToken} />
{/* other fields */}
</form>
// Verify on server
export async function POST(req: Request) {
const formData = await req.formData()
const token = formData.get('csrfToken')
if (!verifyCsrfToken(token)) {
return new Response('Invalid CSRF token', { status: 403 })
}
// Process request
}
5. Security Misconfiguration
Security Headers:
// middleware.ts
export function middleware(request: NextRequest) {
const response = NextResponse.next()
// Security headers
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
response.headers.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()')
// HSTS
response.headers.set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains'
)
return response
}
Authentication Best Practices
OAuth 2.0 Implementation
// Using NextAuth.js
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
})
],
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60 // 30 days
},
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token
}
return token
}
}
})
Rate Limiting
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: 'Too many requests from this IP'
})
// Apply to login endpoint
app.post('/api/login', limiter, handleLogin)
Data Protection
Encryption at Rest
import crypto from 'crypto'
const algorithm = 'aes-256-gcm'
const key = Buffer.from(process.env.ENCRYPTION_KEY!, 'hex')
function encrypt(text: string): string {
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv(algorithm, key, iv)
let encrypted = cipher.update(text, 'utf8', 'hex')
encrypted += cipher.final('hex')
const authTag = cipher.getAuthTag()
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`
}
function decrypt(encryptedData: string): string {
const [ivHex, authTagHex, encrypted] = encryptedData.split(':')
const iv = Buffer.from(ivHex, 'hex')
const authTag = Buffer.from(authTagHex, 'hex')
const decipher = crypto.createDecipheriv(algorithm, key, iv)
decipher.setAuthTag(authTag)
let decrypted = decipher.update(encrypted, 'hex', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}
Encryption in Transit
Always use HTTPS:
// Redirect HTTP to HTTPS
export function middleware(request: NextRequest) {
if (
process.env.NODE_ENV === 'production' &&
request.headers.get('x-forwarded-proto') !== 'https'
) {
return NextResponse.redirect(
`https://${request.headers.get('host')}${request.nextUrl.pathname}`,
301
)
}
}
Input Validation
import { z } from 'zod'
const userSchema = z.object({
email: z.string().email(),
password: z.string().min(8).max(100),
age: z.number().int().positive().max(120)
})
// Validate input
try {
const validData = userSchema.parse(userInput)
// Process valid data
} catch (error) {
// Handle validation errors
return { error: 'Invalid input' }
}
Secure File Uploads
import { extname } from 'path'
const ALLOWED_TYPES = ['.jpg', '.jpeg', '.png', '.pdf']
const MAX_SIZE = 5 * 1024 * 1024 // 5MB
async function validateFile(file: File) {
// Check file size
if (file.size > MAX_SIZE) {
throw new Error('File too large')
}
// Check file extension
const ext = extname(file.name).toLowerCase()
if (!ALLOWED_TYPES.includes(ext)) {
throw new Error('File type not allowed')
}
// Scan for malware (use ClamAV or similar)
await scanForMalware(file)
return true
}
Logging & Monitoring
// Structured logging
import winston from 'winston'
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
})
// Log security events
logger.info('Login attempt', {
userId: user.id,
ip: request.ip,
userAgent: request.headers['user-agent'],
timestamp: new Date().toISOString(),
success: true
})
Dependency Management
# Audit dependencies regularly
npm audit
# Fix vulnerabilities
npm audit fix
# Use Snyk or Dependabot for continuous monitoring
Security Checklist
✅ HTTPS everywhere ✅ Input validation and sanitization ✅ SQL injection prevention ✅ XSS protection ✅ CSRF tokens ✅ Security headers configured ✅ Rate limiting implemented ✅ Strong password policies ✅ Multi-factor authentication ✅ Regular security audits ✅ Dependency updates ✅ Error handling (no sensitive info in errors) ✅ Logging and monitoring ✅ Backup and disaster recovery plan
Tools & Services
Security Scanning:
- OWASP ZAP
- Burp Suite
- Snyk
- SonarQube
Monitoring:
- Sentry
- Datadog
- New Relic
- CloudFlare
Penetration Testing:
- HackerOne
- Bugcrowd
- Cobalt
Need a security audit? Contact us for professional assessment and remediation.
Tags
LetsGrow Dev Team
Marketing Technology Experts
