Security & Compliance
Software Security
Security best practices, common vulnerabilities, and defensive programming
Software Security
Security is critical for protecting user data, maintaining trust, and ensuring system integrity.
OWASP Top 10
1. Broken Access Control
// ❌ Insecure
app.get('/api/users/:id', async (req, res) => {
const user = await db.user.findUnique({
where: { id: req.params.id }
})
res.json(user)
})
// ✅ Secure
app.get('/api/users/:id', authenticate, async (req, res) => {
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' })
}
const user = await db.user.findUnique({
where: { id: req.params.id }
})
res.json(user)
})2. Cryptographic Failures
import bcrypt from 'bcrypt'
// Hash passwords
const hashedPassword = await bcrypt.hash(password, 10)
// Verify passwords
const isValid = await bcrypt.compare(password, hashedPassword)
// Encrypt sensitive data
import crypto from 'crypto'
const algorithm = 'aes-256-gcm'
const key = crypto.randomBytes(32)
const iv = crypto.randomBytes(16)
function encrypt(text: string) {
const cipher = crypto.createCipheriv(algorithm, key, iv)
let encrypted = cipher.update(text, 'utf8', 'hex')
encrypted += cipher.final('hex')
const authTag = cipher.getAuthTag()
return { encrypted, authTag: authTag.toString('hex') }
}3. Injection
// ❌ SQL Injection vulnerable
const query = `SELECT * FROM users WHERE email = '${email}'`
// ✅ Use parameterized queries
const user = await db.user.findUnique({
where: { email }
})
// ✅ Use ORM
const users = await prisma.user.findMany({
where: {
email: {
contains: searchTerm
}
}
})4. Insecure Design
Design security from the start:
- Threat modeling
- Secure architecture patterns
- Defense in depth
- Principle of least privilege
5. Security Misconfiguration
// ❌ Insecure
const app = express()
app.use(cors({ origin: '*' }))
// ✅ Secure
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(','),
credentials: true
}))
// Security headers
import helmet from 'helmet'
app.use(helmet())6. Vulnerable Components
# Audit dependencies
npm audit
pnpm audit
# Update dependencies
npm update
pnpm update
# Use tools like
# - Snyk
# - Dependabot
# - Renovate7. Authentication Failures
// Implement proper authentication
import jwt from 'jsonwebtoken'
function generateToken(userId: string) {
return jwt.sign(
{ userId },
process.env.JWT_SECRET!,
{ expiresIn: '1h' }
)
}
function verifyToken(token: string) {
try {
return jwt.verify(token, process.env.JWT_SECRET!)
} catch (error) {
throw new Error('Invalid 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 windowMs
})
app.use('/api/', limiter)8. Software and Data Integrity
// Verify file integrity
import crypto from 'crypto'
function calculateHash(file: Buffer): string {
return crypto
.createHash('sha256')
.update(file)
.digest('hex')
}
// Verify uploads
const uploadedHash = calculateHash(file)
if (uploadedHash !== expectedHash) {
throw new Error('File integrity check failed')
}9. Logging and Monitoring Failures
// Proper 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.warn('Failed login attempt', {
email: email,
ip: req.ip,
timestamp: new Date()
})10. Server-Side Request Forgery (SSRF)
// Validate and sanitize URLs
import { URL } from 'url'
function isAllowedUrl(urlString: string): boolean {
try {
const url = new URL(urlString)
// Only allow specific protocols
if (!['http:', 'https:'].includes(url.protocol)) {
return false
}
// Block private IP ranges
const hostname = url.hostname
if (
hostname === 'localhost' ||
hostname.startsWith('192.168.') ||
hostname.startsWith('10.') ||
hostname.startsWith('172.')
) {
return false
}
return true
} catch {
return false
}
}Authentication & Authorization
JWT Best Practices
interface TokenPayload {
userId: string
role: string
}
// Short-lived access token
const accessToken = jwt.sign(
payload,
process.env.ACCESS_TOKEN_SECRET!,
{ expiresIn: '15m' }
)
// Long-lived refresh token
const refreshToken = jwt.sign(
payload,
process.env.REFRESH_TOKEN_SECRET!,
{ expiresIn: '7d' }
)Role-Based Access Control (RBAC)
enum Role {
USER = 'user',
ADMIN = 'admin',
MODERATOR = 'moderator'
}
function authorize(allowedRoles: Role[]) {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' })
}
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden' })
}
next()
}
}
app.delete('/api/users/:id', authorize([Role.ADMIN]), deleteUser)Input Validation
Zod Schema Validation
import { z } from 'zod'
const userSchema = z.object({
email: z.string().email(),
password: z.string().min(8).max(100),
age: z.number().min(18).max(120),
username: z.string().regex(/^[a-zA-Z0-9_]+$/)
})
function validateInput(data: unknown) {
try {
return userSchema.parse(data)
} catch (error) {
throw new ValidationError('Invalid input')
}
}Sanitization
import DOMPurify from 'isomorphic-dompurify'
// Sanitize HTML
const clean = DOMPurify.sanitize(dirty)
// Escape SQL
import { escape } from 'sqlstring'
const safe = escape(userInput)Cross-Site Scripting (XSS)
Prevention
// React automatically escapes
<div>{userInput}</div>
// Be careful with dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(html)
}} />
// Content Security Policy
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}))Cross-Site Request Forgery (CSRF)
CSRF Token
import csrf from 'csurf'
const csrfProtection = csrf({ cookie: true })
app.get('/form', csrfProtection, (req, res) => {
res.render('form', { csrfToken: req.csrfToken() })
})
app.post('/process', csrfProtection, (req, res) => {
// Process form
})Secrets Management
Environment Variables
// ❌ Never commit secrets
const apiKey = 'sk_live_abc123'
// ✅ Use environment variables
const apiKey = process.env.API_KEY
// ✅ Use secret management services
// - AWS Secrets Manager
// - HashiCorp Vault
// - Azure Key VaultSecurity Checklist
- Use HTTPS everywhere
- Implement proper authentication
- Validate and sanitize all inputs
- Use parameterized queries
- Keep dependencies updated
- Implement rate limiting
- Use security headers
- Hash passwords with bcrypt/argon2
- Implement CSRF protection
- Log security events
- Regular security audits
- Principle of least privilege
- Data encryption at rest and in transit
Tools
- SAST: SonarQube, Checkmarx
- DAST: OWASP ZAP, Burp Suite
- Dependency Scanning: Snyk, npm audit
- Secrets Scanning: GitGuardian, TruffleHog