Guest User Security Analysis & Hardening Plan

Comprehensive security analysis of guest authentication system with risk mitigation strategies

Guest User Security Analysis & Hardening Plan

Date: November 4, 2025
Status: ๐Ÿ” ANALYSIS COMPLETE - Risks Identified
Priority: ๐Ÿ”ด HIGH - Immediate Action Required


๐ŸŽฏ Executive Summary

Current State: Guest authentication provides a completely barrier-free user experience, allowing unlimited platform access without registration. While this dramatically reduces friction, it introduces significant security risks that need immediate attention.

Risk Level: ๐ŸŸก MEDIUM-HIGH

Key Findings:


๐Ÿ” Current Implementation Analysis

โœ… What's Working (Security Strengths)

1. JWT-Based Authentication โœ…

Status: Strong

// Guest users receive REAL JWT tokens
const payload = { sub: userId, email: user.email, roles: user.roles };
const accessToken = this.jwtService.sign(payload);

Strengths:

Files:


2. Data Isolation โœ…

Status: Strong

// Guest user IDs are prefixed
userId: 'temp-{uuid}'  // e.g., temp-a3f9k2m1
spaceId: 'space_temp-{uuid}'  // e.g., space_temp-a3f9k2m1

// Backend enforces userId-based isolation
const space = await this.spaceModel.findOne({ ownerId: userId }).exec();

Strengths:

Files:


3. Session Management โœ…

Status: Moderate

// 7-day expiration
private readonly SESSION_DURATION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days

const guestUser: GuestUser = {
  userId: `temp-${uuid}`,
  spaceSlug: shortSlug,
  createdAt: now,
  expiresAt: now + this.SESSION_DURATION_MS,  // Auto-expire after 7 days
  lastActivity: now,
  convertedToReal: false
};

Strengths:

Weaknesses:

Files:


4. Guest-to-Authenticated Conversion โœ…

Status: Strong

// Transfer guest space to authenticated user
if (registerDto.guestUserId) {
  const transferredSpace = await this.spacesService.transferGuestSpaceToAuthUser(
    registerDto.guestUserId,
    newUserId
  );
}

Strengths:

Files:


๐Ÿ”ด Critical Security Gaps (Risks)

1. No Rate Limiting ๐Ÿ”ด

Risk Level: ๐Ÿ”ด CRITICAL

Problem:

// โŒ NO RATE LIMITING IN CODE
// Guest users can make unlimited API calls

// Current guard allows ALL requests
if (isGuestUser) {
  return true; // Always allow - NO THROTTLING!
}

Attack Scenarios:

Impact: SEVERE - Service outage, financial loss, database corruption

Files:


2. No Bot Protection ๐Ÿ”ด

Risk Level: ๐Ÿ”ด HIGH

Problem:

// โŒ NO CAPTCHA OR BOT DETECTION
async generateTempUser(): Promise<GuestUser> {
  // Anyone can call this, including bots
  const uuid = uuidv4();
  const guestUser = {
    userId: `temp-${uuid}`,
    // ...
  };
  return guestUser;
}

Attack Scenarios:

Impact: HIGH - Platform abuse, resource exhaustion, degraded UX

Files:


3. Unlimited Permissions (Barrier-Free = Security-Free) ๐ŸŸก

Risk Level: ๐ŸŸก MEDIUM-HIGH

Problem:

// โŒ GUEST USERS GET NEARLY UNLIMITED ACCESS
userContext.setUserContext({
  userId: guestUser.userId,
  spaceId: guestUser.spaceSlug || null,
  isAuthenticated: false,
  permissions: [
    'view:public',
    'create:universe',   // Can create universes
    'create:page',       // Can create pages
    'create:galaxy',     // Can create galaxies
    'edit:own',          // Can edit their data
    'delete:own'         // Can delete their data
  ]
});

// Permission guards ALWAYS allow guests
if (isGuestUser) {
  return true; // Always allow (demo mode)
}

Attack Scenarios:

Impact: MEDIUM - Cost overruns, degraded performance, unfair usage

Files:


4. MongoDB Write Access for Guests ๐ŸŸก

Risk Level: ๐ŸŸก MEDIUM

Problem:

// โœ… FIX: Allow guest users to use API - backend supports guest authentication
// Guest users now get proper JWT tokens, so they can persist data to MongoDB
const isGuestUser = userId.startsWith('temp-');

// Guest users CAN write to MongoDB
if (this._isOnline() && this.spaceApi.isConnected()) {
  await this.spaceApi.createUniverse(spaceId, universe);  // MongoDB write!
}

Attack Scenarios:

Impact: MEDIUM - Increased costs, database management complexity

Note: This is intentional (guests need persistence), but needs controls.

Files:


5. No Audit Logging or Abuse Detection ๐ŸŸก

Risk Level: ๐ŸŸก MEDIUM

Problem:

// โŒ NO ABUSE TRACKING
// We log guest user creation, but no abuse metrics

console.log('๐ŸŽญ Generated temp user:', { userId, spaceSlug });
// No tracking of:
// - API call frequency
// - Resource usage
// - Failed operations
// - Suspicious patterns

Attack Scenarios:

Impact: MEDIUM - Poor incident response, reactive security posture

Files:


6. No IP-Based Tracking or Blacklisting ๐ŸŸก

Risk Level: ๐ŸŸก MEDIUM

Problem:

// โŒ NO IP TRACKING AT ALL
// Same IP can create unlimited guest accounts

Attack Scenarios:

Impact: MEDIUM - Persistent abuse, hard to block bad actors


7. No Cost/Resource Limits ๐ŸŸก

Risk Level: ๐ŸŸก MEDIUM

Problem:

// โŒ NO LIMITS ON GUEST USER RESOURCES
// Can create unlimited:
// - Universes
// - Pages
// - Galaxies
// - Deployments (if enabled)

Attack Scenarios:

Impact: MEDIUM - Financial loss, performance degradation


๐Ÿ›ก๏ธ Security Hardening Plan

Phase 1: Immediate Actions (Week 1) ๐Ÿ”ด

1.1 Implement Rate Limiting

Priority: ๐Ÿ”ด CRITICAL

Implementation:

// New: libs/core-angular/src/lib/guards/rate-limit.guard.ts
import { Injectable } from '@angular/core';
import { GgStorage } from '@storage-system';

interface RateLimitConfig {
  maxRequests: number;  // Max requests per window
  windowMs: number;     // Time window in ms
}

@Injectable({
  providedIn: 'root'
})
export class RateLimitService {
  private storage = inject(GgStorage);
  
  private configs: Record<string, RateLimitConfig> = {
    'guest': { maxRequests: 100, windowMs: 60000 },      // 100 req/min for guests
    'authenticated': { maxRequests: 1000, windowMs: 60000 } // 1000 req/min for auth
  };
  
  async checkRateLimit(userId: string, action: string): Promise<boolean> {
    const isGuest = userId.startsWith('temp-');
    const config = isGuest ? this.configs['guest'] : this.configs['authenticated'];
    
    const key = `rate-limit:${userId}:${action}`;
    const now = Date.now();
    
    // Get existing requests
    const requests = await this.storage.get<number[]>(key) || [];
    
    // Filter out old requests outside the window
    const recentRequests = requests.filter(timestamp => 
      now - timestamp < config.windowMs
    );
    
    // Check if limit exceeded
    if (recentRequests.length >= config.maxRequests) {
      return false; // Rate limit exceeded
    }
    
    // Add current request
    recentRequests.push(now);
    await this.storage.put(key, recentRequests);
    
    return true; // OK
  }
}

Backend Rate Limiting (NestJS):

// apis/backend-api/src/app/common/guards/rate-limit.guard.ts
import { Injectable, CanActivate, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { RateLimiterMemory } from 'rate-limiter-flexible';

@Injectable()
export class RateLimitGuard implements CanActivate {
  private guestLimiter = new RateLimiterMemory({
    points: 100,      // 100 requests
    duration: 60,     // per 60 seconds
  });
  
  private authLimiter = new RateLimiterMemory({
    points: 1000,     // 1000 requests
    duration: 60,     // per 60 seconds
  });
  
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const userId = request.user?.sub || 'anonymous';
    const isGuest = userId.startsWith('temp-');
    
    const limiter = isGuest ? this.guestLimiter : this.authLimiter;
    
    try {
      await limiter.consume(userId);
      return true;
    } catch (error) {
      throw new HttpException('Rate limit exceeded', HttpStatus.TOO_MANY_REQUESTS);
    }
  }
}

Usage:

// Apply to all API routes
@Controller('spaces')
@UseGuards(BackendAuthGuard, RateLimitGuard)  // โ† Add rate limiting
export class SpacesController {
  // ...
}

Files to Create:

Dependencies:

npm install rate-limiter-flexible

1.2 Add Bot Protection (CAPTCHA)

Priority: ๐Ÿ”ด HIGH

Implementation (Google reCAPTCHA v3):

// libs/auth-ui/src/lib/services/guest-auth.service.ts
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

async generateTempUser(): Promise<GuestUser> {
  // โœ… NEW: Verify CAPTCHA before creating guest user
  const recaptchaToken = await this.getRecaptchaToken();
  const isHuman = await this.verifyRecaptcha(recaptchaToken);
  
  if (!isHuman) {
    throw new Error('Bot detection failed. Please try again.');
  }
  
  // Proceed with guest user creation
  const uuid = uuidv4();
  // ...
}

private async getRecaptchaToken(): Promise<string> {
  return new Promise((resolve, reject) => {
    grecaptcha.ready(() => {
      grecaptcha.execute('YOUR_SITE_KEY', { action: 'guest_signup' })
        .then(token => resolve(token))
        .catch(error => reject(error));
    });
  });
}

private async verifyRecaptcha(token: string): Promise<boolean> {
  // Backend verification
  const result = await this.http.post<{success: boolean, score: number}>(
    '/api/auth/verify-recaptcha',
    { token }
  ).toPromise();
  
  // reCAPTCHA v3 returns a score 0.0-1.0
  // 1.0 = very likely human, 0.0 = very likely bot
  return result.success && result.score >= 0.5;
}

Backend Verification:

// apis/backend-api/src/app/auth/auth.controller.ts
@Post('verify-recaptcha')
async verifyRecaptcha(@Body() body: { token: string }): Promise<{success: boolean, score: number}> {
  const secretKey = this.configService.get<string>('RECAPTCHA_SECRET_KEY');
  
  const response = await this.httpService.post(
    'https://www.google.com/recaptcha/api/siteverify',
    null,
    {
      params: {
        secret: secretKey,
        response: body.token
      }
    }
  ).toPromise();
  
  return {
    success: response.data.success,
    score: response.data.score
  };
}

Environment Configuration:

// apps/gograb-space/src/environments/environment.ts
export const environment = {
  // ...
  recaptcha: {
    siteKey: 'YOUR_RECAPTCHA_SITE_KEY',
    enabled: true
  }
};

Files to Modify:

Dependencies:

npm install @google-cloud/recaptcha-enterprise

Phase 2: Resource Management (Week 2) ๐ŸŸก

2.1 Implement Resource Quotas

Priority: ๐ŸŸก HIGH

Implementation:

// New: libs/core-angular/src/lib/services/resource-quota.service.ts
export interface ResourceQuota {
  maxUniverses: number;
  maxPages: number;
  maxGalaxies: number;
  maxStorageMB: number;
  maxApiCallsPerDay: number;
}

@Injectable({
  providedIn: 'root'
})
export class ResourceQuotaService {
  private quotas: Record<string, ResourceQuota> = {
    'guest': {
      maxUniverses: 3,
      maxPages: 10,
      maxGalaxies: 5,
      maxStorageMB: 50,
      maxApiCallsPerDay: 500
    },
    'free': {
      maxUniverses: 10,
      maxPages: 100,
      maxGalaxies: 50,
      maxStorageMB: 500,
      maxApiCallsPerDay: 5000
    },
    'pro': {
      maxUniverses: -1,  // unlimited
      maxPages: -1,
      maxGalaxies: -1,
      maxStorageMB: -1,
      maxApiCallsPerDay: -1
    }
  };
  
  async checkQuota(
    userId: string,
    resource: keyof ResourceQuota,
    currentUsage: number
  ): Promise<boolean> {
    const isGuest = userId.startsWith('temp-');
    const tier = isGuest ? 'guest' : 'free'; // Assume free for authenticated
    
    const quota = this.quotas[tier];
    const limit = quota[resource];
    
    // -1 means unlimited
    if (limit === -1) {
      return true;
    }
    
    return currentUsage < limit;
  }
  
  async getUsage(userId: string): Promise<Record<keyof ResourceQuota, number>> {
    // Query current usage from database
    const spaces = await this.spaceService.loadSpacesForUser(userId);
    const universes = await this.spaceService.loadUniverses();
    
    return {
      maxUniverses: universes.length,
      maxPages: 0, // TODO: Count pages
      maxGalaxies: 0, // TODO: Count galaxies
      maxStorageMB: 0, // TODO: Calculate storage
      maxApiCallsPerDay: 0 // TODO: Count API calls
    };
  }
}

Usage in SpaceService:

// libs/core-angular/src/lib/services/space.service.ts
async createUniverse(data: { name: string; description?: string }): Promise<UniverseWithDeployment> {
  // โœ… NEW: Check quota before creating
  const currentUsage = await this.resourceQuota.getUsage(this.userContext.userId());
  const canCreate = await this.resourceQuota.checkQuota(
    this.userContext.userId(),
    'maxUniverses',
    currentUsage.maxUniverses
  );
  
  if (!canCreate) {
    throw new Error('Universe quota exceeded. Please upgrade to create more universes.');
  }
  
  // Proceed with creation
  // ...
}

Files to Create:

Files to Modify:


2.2 Shorten Guest Session Duration

Priority: ๐ŸŸก MEDIUM

Change:

// libs/auth-ui/src/lib/services/guest-auth.service.ts
// BEFORE:
private readonly SESSION_DURATION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days

// AFTER:
private readonly SESSION_DURATION_MS = 2 * 24 * 60 * 60 * 1000; // 2 days (industry standard)

Rationale:

Files to Modify:


Phase 3: Monitoring & Audit (Week 3) ๐ŸŸก

3.1 Implement Audit Logging

Priority: ๐ŸŸก HIGH

Implementation:

// New: libs/core-angular/src/lib/services/audit-log.service.ts
export interface AuditEvent {
  timestamp: number;
  userId: string;
  action: string;
  resource: string;
  details: any;
  ipAddress?: string;
  userAgent?: string;
}

@Injectable({
  providedIn: 'root'
})
export class AuditLogService {
  private storage = inject(GgStorage);
  private http = inject(HttpClient);
  
  async log(event: Omit<AuditEvent, 'timestamp'>): Promise<void> {
    const auditEvent: AuditEvent = {
      ...event,
      timestamp: Date.now()
    };
    
    // Store locally
    await this.storage.put(`audit-log:${event.userId}:${auditEvent.timestamp}`, auditEvent);
    
    // Send to backend (async, non-blocking)
    this.http.post('/api/audit-log', auditEvent).subscribe();
  }
  
  async getLogsForUser(userId: string, limit: number = 100): Promise<AuditEvent[]> {
    // Query from backend
    return this.http.get<AuditEvent[]>(`/api/audit-log/${userId}?limit=${limit}`).toPromise();
  }
}

Usage:

// Log all significant actions
async createUniverse(data: { name: string }): Promise<UniverseWithDeployment> {
  // Create universe
  const universe = await this.syncEngine.write(/* ... */);
  
  // โœ… NEW: Log action
  await this.auditLog.log({
    userId: this.userContext.userId(),
    action: 'create',
    resource: 'universe',
    details: { universeId: universe.id, name: data.name }
  });
  
  return universe;
}

Files to Create:


3.2 Add Abuse Detection

Priority: ๐ŸŸก MEDIUM

Implementation:

// New: apis/backend-api/src/app/abuse-detection/abuse-detection.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AbuseDetectionService {
  // Configurable thresholds
  private thresholds = {
    maxCreationsPerHour: 20,
    maxFailedRequestsPerHour: 50,
    maxStorageGrowthMBPerHour: 100
  };
  
  async checkForAbuse(userId: string): Promise<{isAbuser: boolean, reason?: string}> {
    const recentActivity = await this.getRecentActivity(userId);
    
    // Check creation rate
    if (recentActivity.creations > this.thresholds.maxCreationsPerHour) {
      return { isAbuser: true, reason: 'Excessive resource creation' };
    }
    
    // Check failed requests
    if (recentActivity.failedRequests > this.thresholds.maxFailedRequestsPerHour) {
      return { isAbuser: true, reason: 'Too many failed requests' };
    }
    
    // Check storage growth
    if (recentActivity.storageGrowthMB > this.thresholds.maxStorageGrowthMBPerHour) {
      return { isAbuser: true, reason: 'Abnormal storage growth' };
    }
    
    return { isAbuser: false };
  }
  
  async blockUser(userId: string, reason: string): Promise<void> {
    // Add to blacklist
    await this.blockedUsersModel.create({
      userId,
      reason,
      blockedAt: new Date()
    });
    
    // Notify admins
    await this.notificationService.notifyAdmins({
      title: 'User Blocked for Abuse',
      message: `User ${userId} blocked: ${reason}`
    });
  }
}

Files to Create:


Phase 4: Advanced Protection (Week 4) ๐Ÿ”ต

4.1 IP-Based Tracking & Blacklisting

Priority: ๐Ÿ”ต MEDIUM

Implementation:

// New: apis/backend-api/src/app/common/guards/ip-blacklist.guard.ts
@Injectable()
export class IpBlacklistGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const ip = this.getClientIp(request);
    
    // Check if IP is blacklisted
    const isBlacklisted = await this.ipBlacklistService.isBlacklisted(ip);
    
    if (isBlacklisted) {
      throw new HttpException('Access denied', HttpStatus.FORBIDDEN);
    }
    
    // Track IP usage
    await this.ipTrackingService.recordRequest(ip, request.user?.sub);
    
    return true;
  }
  
  private getClientIp(request: any): string {
    return request.headers['x-forwarded-for']?.split(',')[0] ||
           request.headers['x-real-ip'] ||
           request.connection.remoteAddress;
  }
}

Files to Create:


4.2 Automated Cleanup of Expired Guest Data

Priority: ๐Ÿ”ต LOW

Implementation:

// New: apis/backend-api/src/app/tasks/cleanup-expired-guests.task.ts
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class CleanupExpiredGuestsTask {
  @Cron(CronExpression.EVERY_DAY_AT_3AM)
  async handleCron() {
    const now = Date.now();
    const sevenDaysAgo = now - (7 * 24 * 60 * 60 * 1000);
    
    // Find expired guest spaces
    const expiredSpaces = await this.spaceModel.find({
      ownerId: /^temp-/,
      updatedAt: { $lt: new Date(sevenDaysAgo) }
    }).exec();
    
    // Delete expired spaces and their data
    for (const space of expiredSpaces) {
      await this.spacesService.deleteSpace(space.id, space.ownerId);
    }
    
    console.log(`๐Ÿงน Cleaned up ${expiredSpaces.length} expired guest spaces`);
  }
}

Files to Create:


๐Ÿ“Š Implementation Priority Matrix

Feature Priority Effort Impact Timeline
Rate Limiting ๐Ÿ”ด Critical Medium Critical Week 1
Bot Protection (CAPTCHA) ๐Ÿ”ด High Medium High Week 1
Resource Quotas ๐ŸŸก High High High Week 2
Shorten Sessions (7d โ†’ 2d) ๐ŸŸก Medium Low Medium Week 2
Audit Logging ๐ŸŸก High Medium Medium Week 3
Abuse Detection ๐ŸŸก Medium High Medium Week 3
IP Blacklisting ๐Ÿ”ต Medium Medium Low Week 4
Automated Cleanup ๐Ÿ”ต Low Low Low Week 4

โœ… Recommended Configuration

Production Settings

// Recommended production configuration
export const SECURITY_CONFIG = {
  guest: {
    // Session
    sessionDuration: 2 * 24 * 60 * 60 * 1000, // 2 days (not 7)
    
    // Rate Limiting
    maxRequestsPerMinute: 60,
    maxCreationsPerHour: 10,
    
    // Resource Quotas
    maxUniverses: 2,
    maxPages: 5,
    maxGalaxies: 3,
    maxStorageMB: 25,
    
    // Bot Protection
    requireCaptcha: true,
    captchaMinScore: 0.5,
    
    // Monitoring
    enableAuditLog: true,
    enableAbuseDetection: true
  },
  
  authenticated: {
    // Free Tier
    free: {
      maxUniverses: 10,
      maxPages: 100,
      maxGalaxies: 50,
      maxStorageMB: 500,
      maxRequestsPerMinute: 300
    },
    
    // Pro Tier
    pro: {
      maxUniverses: -1, // unlimited
      maxPages: -1,
      maxGalaxies: -1,
      maxStorageMB: -1,
      maxRequestsPerMinute: 1000
    }
  }
};

๐ŸŽฏ Success Metrics

Security KPIs

Metric Target Measurement
Bot Detection Rate > 95% CAPTCHA rejections
Abuse Detection Rate > 90% Automated blocking
Rate Limit Violations < 1% Throttled requests
Guest Conversion Rate > 10% Guest โ†’ Authenticated
Average Session Duration 1-3 days Time to conversion
Resource Quota Violations < 5% Quota exceeded errors

๐Ÿ“š Related Documentation


๐Ÿ”’ Final Recommendations

Immediate Actions (This Week)

  1. โœ… Implement Rate Limiting - Prevents API flooding
  2. โœ… Add CAPTCHA - Blocks bot attacks
  3. โœ… Reduce Session Duration - 7d โ†’ 2d (industry standard)

Short-Term (Next Month)

  1. โœ… Resource Quotas - Prevents storage/compute abuse
  2. โœ… Audit Logging - Enables forensics and monitoring
  3. โœ… Abuse Detection - Automated threat response

Long-Term (Next Quarter)

  1. โœ… IP Blacklisting - Geographic and persistent blocking
  2. โœ… Automated Cleanup - Reduces database bloat
  3. โœ… Cost Analytics - Track guest vs. authenticated costs

Last Updated: November 4, 2025
Status: ๐Ÿ” ANALYSIS COMPLETE - Implementation Pending
Risk Assessment: ๐ŸŸก MEDIUM-HIGH - Action Required