This document outlines the security features and best practices implemented in ludiscan-webapp.
httpOnly Cookies for Token Storage
- Authentication tokens are stored in httpOnly cookies, preventing JavaScript access
- Protects against XSS (Cross-Site Scripting) attacks
- Cookies are automatically included in requests with
credentials: 'include'
Implementation:
// Login via secure API route
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include', // Include cookies
body: JSON.stringify({ email, password }),
});Cookie Settings:
httpOnly: true- Prevents JavaScript accesssecure: true- HTTPS only (production)sameSite: 'lax'- CSRF protectionmaxAge: 7 days- Auto-expiration
Social Login (OAuth) Security
For Google and other OAuth providers:
// Flow:
// 1. User clicks "Login with Google" -> redirects to backend OAuth URL
// 2. Backend handles OAuth and redirects to /api/auth/social-callback?token=xxx
// 3. API route validates token, sets httpOnly cookie, redirects to /auth/social-callback
// 4. Page validates session and redirects to home
// Token is NEVER exposed to client JavaScriptAPI Route: /api/auth/social-callback
- Validates token with backend
- Sets httpOnly cookie
- Generates CSRF token
- Redirects to success page
Client Page: /auth/social-callback
- Validates session using httpOnly cookie
- No token handling in client code
- Automatic redirect after validation
Double Submit Cookie Pattern
- CSRF tokens generated for sensitive operations
- Token stored in both cookie and request header
- Server validates token matches
Implementation:
// Get CSRF token
const { csrfToken } = await fetch('/api/auth/csrf').then(r => r.json());
// Include in requests
fetch('/api/protected', {
headers: { 'X-CSRF-Token': csrfToken },
credentials: 'include',
});Content Security Policy (CSP)
- Prevents XSS attacks by restricting resource loading
- Blocks inline scripts (except whitelisted)
- Enforces HTTPS upgrades
Headers Configured:
Content-Security-Policy- XSS and injection protectionX-Frame-Options: DENY- Clickjacking protectionX-Content-Type-Options: nosniff- MIME sniffing protectionStrict-Transport-Security- HTTPS enforcement (production)Referrer-Policy- Control referrer informationPermissions-Policy- Disable unnecessary browser features
Configuration: See next.config.ts
API Route Protection
- In-memory rate limiting for all API endpoints
- Different limits for different endpoint types
Rate Limits:
- Authentication endpoints: 5 requests/minute
- General API endpoints: 30 requests/minute
- Read-only endpoints: 100 requests/minute
Implementation:
import { rateLimitMiddleware, RATE_LIMITS } from '@src/utils/security/rateLimit';
export default async function handler(req, res) {
const rateLimit = rateLimitMiddleware(RATE_LIMITS.AUTH)(req, res);
if (!rateLimit.allowed) return; // 429 response sent automatically
// Your API logic here
}Note: For production with multiple servers, consider Redis-based rate limiting.
Zod Schema Validation
- Environment variables validated with flexible build/runtime behavior
- Uses default values during CI/CD builds (allows empty .env)
- Validates strictly at production runtime with warnings
- Type-safe access throughout application
Configuration: See src/config/env.ts
import { env } from '@src/config/env';
// env.NEXT_PUBLIC_API_BASE_URL is validated and type-safeBuild vs Runtime:
- Build time (CI/CD): Uses defaults if env vars not set β build succeeds
- Runtime (production): Validates actual values β warns if invalid (doesn't crash)
- This allows CI builds without exposing secrets in build logs
Secure Backend Communication
- Generic proxy at
/api/proxy/[...path]forwards requests to backend - Automatically injects auth token from httpOnly cookie
- Client never directly handles tokens
Usage:
// Instead of: fetch('https://backend.com/api/v0/user/me')
// Use: fetch('/api/proxy/v0/user/me')-
Never Store Sensitive Data in localStorage
- Use httpOnly cookies for tokens
- localStorage is vulnerable to XSS
-
Always Validate Input
- Validate on both client and server
- Use Zod or similar validation libraries
-
Use Rate Limiting
- Apply to all API routes
- Adjust limits based on endpoint sensitivity
-
Keep Dependencies Updated
bun update bun audit
-
Review Security Headers
- Test with securityheaders.com
- Adjust CSP as needed for new integrations
-
Use HTTPS in Production
- Never deploy without HTTPS
- Enable HSTS header
-
Rotate Secrets Regularly
- SESSION_SECRET
- CSRF_SECRET
- API keys
-
Set Secure Environment Variables
# Generate secure random strings node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
-
Required Environment Variables
SESSION_SECRET=<32+ character random string> CSRF_SECRET=<32+ character random string> NEXT_PUBLIC_HOSTNAME=<your-domain> NEXT_PUBLIC_API_BASE_URL=<backend-api-url>
-
Enable Security Features
- Ensure
NODE_ENV=production - Verify HTTPS is enabled
- Check security headers are applied
- Ensure
-
Monitor & Log
- Monitor rate limit hits
- Log authentication attempts
- Set up alerts for suspicious activity
Before deploying to production:
- Environment variables validated with Zod
- SESSION_SECRET and CSRF_SECRET set to secure random values (32+ chars)
- HTTPS enabled and working
- Security headers tested (use securityheaders.com)
- Rate limiting configured for all API routes
- Dependencies updated and audited
- No secrets committed to repository
- Authentication uses httpOnly cookies
- CSRF protection enabled for state-changing operations
- Error messages don't leak sensitive information
- CORS properly configured
If you discover a security vulnerability, please email security@ludiscan.com (or appropriate contact).
Do not:
- Open a public GitHub issue
- Disclose the vulnerability publicly before it's fixed
Do:
- Provide detailed information about the vulnerability
- Include steps to reproduce
- Suggest a fix if possible
| Date | Version | Changes | Auditor |
|---|---|---|---|
| 2025-11-20 | v0.18.0+ | Initial security implementation: httpOnly cookies, CSRF protection, rate limiting, security headers | Claude |
Last Updated: 2025-11-20 Next Review: 2026-02-20 (quarterly reviews recommended)