Skip to main content

Security Implementation Guide

Overview​

This guide provides detailed implementation steps for securing both toto-app and toto-bo applications to production standards.

1. Authentication & Authorization​

Multi-Factor Authentication (MFA)​

toto-app Implementation​

// src/lib/auth/mfa.ts
import { getAuth, multiFactor, PhoneAuthProvider } from 'firebase/auth';

export class MFAHandler {
private auth = getAuth();

async enrollMFA(phoneNumber: string): Promise<void> {
const user = this.auth.currentUser;
if (!user) throw new Error('User not authenticated');

const multiFactorSession = await multiFactor(user).getSession();
const phoneAuthCredential = PhoneAuthProvider.credential(
verificationId,
verificationCode
);

const multiFactorAssertion = PhoneAuthProvider.credential(
phoneAuthCredential
);

await multiFactor(user).enroll(multiFactorAssertion, 'Phone Number');
}

async verifyMFA(verificationId: string, verificationCode: string): Promise<boolean> {
try {
const user = this.auth.currentUser;
if (!user) return false;

const multiFactorAssertion = PhoneAuthProvider.credential(
verificationId,
verificationCode
);

await multiFactor(user).assertEnrolled(multiFactorAssertion);
return true;
} catch (error) {
console.error('MFA verification failed:', error);
return false;
}
}
}

toto-bo Implementation​

// src/lib/auth/mfa.ts
import { NextAuthOptions } from 'next-auth';
import { authenticator } from 'otplib';

export const mfaOptions: NextAuthOptions = {
callbacks: {
async jwt({ token, user }) {
if (user) {
token.mfaEnabled = user.mfaEnabled;
token.mfaVerified = false;
}
return token;
},
async session({ session, token }) {
session.user.mfaEnabled = token.mfaEnabled;
session.user.mfaVerified = token.mfaVerified;
return session;
}
}
};

export class MFAHandler {
static generateSecret(): string {
return authenticator.generateSecret();
}

static generateQRCode(secret: string, email: string): string {
return authenticator.keyuri(email, 'Toto Backoffice', secret);
}

static verifyToken(token: string, secret: string): boolean {
return authenticator.verify({ token, secret });
}
}

Role-Based Access Control (RBAC)​

toto-app RBAC Implementation​

// src/lib/auth/rbac.ts
export enum UserRole {
ADMIN = 'admin',
STAFF = 'staff',
GUARDIAN = 'guardian',
USER = 'user'
}

export enum Permission {
CREATE_CASE = 'create:case',
READ_CASE = 'read:case',
UPDATE_CASE = 'update:case',
DELETE_CASE = 'delete:case',
MANAGE_USERS = 'manage:users',
VIEW_ANALYTICS = 'view:analytics'
}

export const rolePermissions: Record<UserRole, Permission[]> = {
[UserRole.ADMIN]: Object.values(Permission),
[UserRole.STAFF]: [
Permission.CREATE_CASE,
Permission.READ_CASE,
Permission.UPDATE_CASE,
Permission.VIEW_ANALYTICS
],
[UserRole.GUARDIAN]: [
Permission.CREATE_CASE,
Permission.READ_CASE,
Permission.UPDATE_CASE
],
[UserRole.USER]: [
Permission.READ_CASE
]
};

export function hasPermission(userRole: UserRole, permission: Permission): boolean {
return rolePermissions[userRole]?.includes(permission) || false;
}

export function requirePermission(permission: Permission) {
return (req: NextRequest, user: any) => {
if (!hasPermission(user.role, permission)) {
throw new Error('Insufficient permissions');
}
};
}

toto-bo RBAC Implementation​

// src/lib/auth/rbac.ts
export enum BackofficeRole {
SUPER_ADMIN = 'super_admin',
ADMIN = 'admin',
MANAGER = 'manager',
STAFF = 'staff',
VIEWER = 'viewer'
}

export enum BackofficePermission {
MANAGE_USERS = 'manage:users',
MANAGE_CASES = 'manage:cases',
VIEW_ANALYTICS = 'view:analytics',
MANAGE_SETTINGS = 'manage:settings',
VIEW_LOGS = 'view:logs',
MANAGE_ALERTS = 'manage:alerts'
}

export const backofficeRolePermissions: Record<BackofficeRole, BackofficePermission[]> = {
[BackofficeRole.SUPER_ADMIN]: Object.values(BackofficePermission),
[BackofficeRole.ADMIN]: [
BackofficePermission.MANAGE_USERS,
BackofficePermission.MANAGE_CASES,
BackofficePermission.VIEW_ANALYTICS,
BackofficePermission.VIEW_LOGS
],
[BackofficeRole.MANAGER]: [
BackofficePermission.MANAGE_CASES,
BackofficePermission.VIEW_ANALYTICS
],
[BackofficeRole.STAFF]: [
BackofficePermission.MANAGE_CASES
],
[BackofficeRole.VIEWER]: [
BackofficePermission.VIEW_ANALYTICS
]
};

2. API Security​

Rate Limiting Implementation​

toto-app Rate Limiting​

// src/lib/security/rateLimiter.ts
import { NextRequest, NextResponse } from 'next/server';
import { Redis } from 'ioredis';

const redis = new Redis(process.env.REDIS_URL!);

export class RateLimiter {
private windowMs: number;
private maxRequests: number;

constructor(windowMs: number = 15 * 60 * 1000, maxRequests: number = 100) {
this.windowMs = windowMs;
this.maxRequests = maxRequests;
}

async checkLimit(identifier: string): Promise<{ allowed: boolean; remaining: number; resetTime: number }> {
const key = `rate_limit:${identifier}`;
const now = Date.now();
const windowStart = now - this.windowMs;

// Remove expired entries
await redis.zremrangebyscore(key, 0, windowStart);

// Count current requests
const currentCount = await redis.zcard(key);

if (currentCount >= this.maxRequests) {
const oldestRequest = await redis.zrange(key, 0, 0, 'WITHSCORES');
const resetTime = oldestRequest.length > 0 ?
parseInt(oldestRequest[1]) + this.windowMs :
now + this.windowMs;

return {
allowed: false,
remaining: 0,
resetTime
};
}

// Add current request
await redis.zadd(key, now, now);
await redis.expire(key, Math.ceil(this.windowMs / 1000));

return {
allowed: true,
remaining: this.maxRequests - currentCount - 1,
resetTime: now + this.windowMs
};
}
}

export const rateLimiter = new RateLimiter();

export async function withRateLimit(
req: NextRequest,
identifier: string = req.ip || 'unknown'
) {
const limit = await rateLimiter.checkLimit(identifier);

if (!limit.allowed) {
return NextResponse.json(
{ error: 'Rate limit exceeded' },
{
status: 429,
headers: {
'X-RateLimit-Limit': '100',
'X-RateLimit-Remaining': '0',
'X-RateLimit-Reset': limit.resetTime.toString(),
'Retry-After': Math.ceil((limit.resetTime - Date.now()) / 1000).toString()
}
}
);
}

return null;
}

Input Validation & Sanitization​

toto-app Input Validation​

// src/lib/security/validation.ts
import { z } from 'zod';
import DOMPurify from 'isomorphic-dompurify';

export const caseSchema = z.object({
title: z.string()
.min(1, 'Title is required')
.max(100, 'Title too long')
.transform(val => DOMPurify.sanitize(val)),
description: z.string()
.min(10, 'Description too short')
.max(1000, 'Description too long')
.transform(val => DOMPurify.sanitize(val)),
location: z.string()
.min(1, 'Location is required')
.max(100, 'Location too long'),
animalType: z.enum(['dog', 'cat', 'bird', 'other']),
urgency: z.enum(['low', 'medium', 'high', 'critical']),
images: z.array(z.string().url()).max(5, 'Too many images')
});

export const donationSchema = z.object({
amount: z.number()
.min(1, 'Amount must be positive')
.max(10000, 'Amount too large'),
currency: z.enum(['USD', 'EUR', 'GBP']),
donorEmail: z.string().email('Invalid email'),
caseId: z.string().uuid('Invalid case ID'),
paymentMethod: z.enum(['stripe', 'stellar'])
});

export function validateInput<T>(schema: z.ZodSchema<T>, data: unknown): T {
try {
return schema.parse(data);
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(`Validation error: ${error.errors.map(e => e.message).join(', ')}`);
}
throw error;
}
}

3. Security Headers​

Next.js Security Headers Configuration​

toto-app Security Headers​

// next.config.js
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-DNS-Prefetch-Control',
value: 'on'
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
},
{
key: 'X-XSS-Protection',
value: '1; mode=block'
},
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin'
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()'
},
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https: blob:",
"connect-src 'self' https://api.stripe.com https://horizon.stellar.org",
"frame-src 'self' https://js.stripe.com",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'none'"
].join('; ')
}
]
}
];
}
};

4. Data Protection​

Encryption Implementation​

Sensitive Data Encryption​

// src/lib/security/encryption.ts
import crypto from 'crypto';

const algorithm = 'aes-256-gcm';
const keyLength = 32;
const ivLength = 16;
const tagLength = 16;

export class EncryptionService {
private key: Buffer;

constructor() {
this.key = crypto.scryptSync(process.env.ENCRYPTION_KEY!, 'salt', keyLength);
}

encrypt(text: string): { encrypted: string; iv: string; tag: string } {
const iv = crypto.randomBytes(ivLength);
const cipher = crypto.createCipher(algorithm, this.key);
cipher.setAAD(Buffer.from('toto-app', 'utf8'));

let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');

const tag = cipher.getAuthTag();

return {
encrypted,
iv: iv.toString('hex'),
tag: tag.toString('hex')
};
}

decrypt(encrypted: string, iv: string, tag: string): string {
const decipher = crypto.createDecipher(algorithm, this.key);
decipher.setAAD(Buffer.from('toto-app', 'utf8'));
decipher.setAuthTag(Buffer.from(tag, 'hex'));

let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');

return decrypted;
}
}

export const encryptionService = new EncryptionService();

PII Data Handling​

Data Anonymization​

// src/lib/security/pii.ts
export class PIIHandler {
static anonymizeEmail(email: string): string {
const [local, domain] = email.split('@');
const anonymizedLocal = local.length > 2
? local[0] + '*'.repeat(local.length - 2) + local[local.length - 1]
: local;
return `${anonymizedLocal}@${domain}`;
}

static anonymizePhone(phone: string): string {
return phone.replace(/(\d{3})\d{4}(\d{3})/, '$1****$2');
}

static hashIdentifier(identifier: string): string {
return crypto.createHash('sha256')
.update(identifier + process.env.HASH_SALT!)
.digest('hex');
}

static shouldRetainData(dataType: string, retentionPeriod: number): boolean {
// Implement GDPR data retention logic
return true; // Simplified for example
}
}

5. Audit Logging​

Security Event Logging​

Audit Logger Implementation​

// src/lib/security/audit.ts
import { db } from '@/lib/firebase';

export enum SecurityEvent {
LOGIN_SUCCESS = 'login_success',
LOGIN_FAILURE = 'login_failure',
LOGOUT = 'logout',
PERMISSION_DENIED = 'permission_denied',
DATA_ACCESS = 'data_access',
DATA_MODIFICATION = 'data_modification',
SUSPICIOUS_ACTIVITY = 'suspicious_activity'
}

export interface AuditLog {
id: string;
event: SecurityEvent;
userId?: string;
ipAddress: string;
userAgent: string;
resource?: string;
resourceId?: string;
details: Record<string, any>;
timestamp: Date;
severity: 'low' | 'medium' | 'high' | 'critical';
}

export class AuditLogger {
static async log(event: SecurityEvent, details: AuditLog): Promise<void> {
try {
const auditLog: AuditLog = {
id: crypto.randomUUID(),
event,
...details,
timestamp: new Date()
};

await db.collection('audit_logs').add(auditLog);
} catch (error) {
console.error('Failed to log audit event:', error);
}
}

static async getAuditLogs(
userId?: string,
event?: SecurityEvent,
startDate?: Date,
endDate?: Date
): Promise<AuditLog[]> {
let query = db.collection('audit_logs');

if (userId) query = query.where('userId', '==', userId);
if (event) query = query.where('event', '==', event);
if (startDate) query = query.where('timestamp', '>=', startDate);
if (endDate) query = query.where('timestamp', '<=', endDate);

const snapshot = await query.orderBy('timestamp', 'desc').limit(1000).get();
return snapshot.docs.map(doc => doc.data() as AuditLog);
}
}

6. Security Monitoring​

Intrusion Detection​

Security Monitoring Service​

// src/lib/security/monitoring.ts
export class SecurityMonitor {
private static suspiciousPatterns = [
/admin/i,
/script/i,
/<script/i,
/javascript:/i,
/onload=/i,
/onerror=/i
];

static detectSuspiciousActivity(
input: string,
context: { endpoint: string; userId?: string; ipAddress: string }
): boolean {
// Check for suspicious patterns
const hasSuspiciousPattern = this.suspiciousPatterns.some(pattern =>
pattern.test(input)
);

if (hasSuspiciousPattern) {
this.alertSecurityTeam({
type: 'suspicious_input',
input,
context,
severity: 'high'
});
return true;
}

return false;
}

static async alertSecurityTeam(alert: {
type: string;
input?: string;
context: any;
severity: 'low' | 'medium' | 'high' | 'critical';
}): Promise<void> {
// Send alert to security team
console.error('SECURITY ALERT:', alert);

// Log to audit system
await AuditLogger.log(SecurityEvent.SUSPICIOUS_ACTIVITY, {
event: SecurityEvent.SUSPICIOUS_ACTIVITY,
ipAddress: alert.context.ipAddress,
userAgent: alert.context.userAgent || 'unknown',
details: alert,
severity: alert.severity
});
}
}

7. Security Testing​

Automated Security Tests​

Security Test Suite​

// tests/security/security.test.ts
import { describe, it, expect } from '@jest/globals';
import { validateInput, caseSchema } from '@/lib/security/validation';
import { hasPermission, UserRole, Permission } from '@/lib/auth/rbac';

describe('Security Tests', () => {
describe('Input Validation', () => {
it('should reject XSS attempts', () => {
const maliciousInput = {
title: '<script>alert("xss")</script>',
description: 'Normal description',
location: 'Test Location',
animalType: 'dog',
urgency: 'medium',
images: []
};

expect(() => validateInput(caseSchema, maliciousInput)).toThrow();
});

it('should sanitize HTML content', () => {
const input = {
title: 'Test <b>Bold</b> Title',
description: 'Description with <i>italic</i> text',
location: 'Test Location',
animalType: 'dog',
urgency: 'medium',
images: []
};

const result = validateInput(caseSchema, input);
expect(result.title).not.toContain('<b>');
expect(result.description).not.toContain('<i>');
});
});

describe('RBAC', () => {
it('should enforce role-based permissions', () => {
expect(hasPermission(UserRole.USER, Permission.CREATE_CASE)).toBe(false);
expect(hasPermission(UserRole.GUARDIAN, Permission.CREATE_CASE)).toBe(true);
expect(hasPermission(UserRole.ADMIN, Permission.MANAGE_USERS)).toBe(true);
});
});
});

8. Security Checklist​

Pre-Production Security Checklist​

  • Authentication

    • MFA implemented for admin users
    • Strong password policies enforced
    • Session timeout configured
    • Account lockout after failed attempts
  • Authorization

    • RBAC implemented and tested
    • API endpoints protected
    • Resource-level permissions enforced
    • Privilege escalation prevented
  • Input Validation

    • All inputs validated and sanitized
    • XSS protection implemented
    • SQL injection prevention
    • File upload restrictions
  • Data Protection

    • Sensitive data encrypted
    • PII handling compliant
    • Data retention policies
    • Secure data transmission
  • Infrastructure Security

    • Security headers configured
    • CORS properly configured
    • Rate limiting implemented
    • Monitoring and alerting active
  • Compliance

    • GDPR compliance verified
    • PCI DSS compliance (if applicable)
    • Audit logging implemented
    • Security documentation complete

This security implementation guide ensures both toto-app and toto-bo meet enterprise-grade security standards for production deployment.