const LessonModule = require('../base/LessonModule'); /** * IDOR (Insecure Direct Object Reference) Demo Lesson * Demonstrates how URL parameter manipulation can expose other users' data */ class IDORDemoLesson extends LessonModule { constructor(config) { super(config); } /** * Get mock user database * @returns {Array} Mock user data */ getMockUsers() { return [ { id: 1, name: 'System Administrator', email: 'admin@securebank.example', accountBalance: '$999,999.99', accountNumber: '****0001', lastLogin: '2026-02-08 10:00', address: '1 Admin Tower, Capital City, USA', phone: '(555) 000-0001', isCurrentUser: false, accountType: 'Administrative Account', isHighValue: true, adminAccess: true, isEasterEgg: true, easterEggType: 'admin', bonusPoints: 25, easterEggMessage: '🎯 Admin Account Found! You discovered the system administrator account.' }, { id: 42, name: 'Douglas Adams', email: 'dont.panic@example.com', accountBalance: '$42,000,000.00', accountNumber: '****4242', lastLogin: '2026-02-07 16:45', address: '42 Galaxy Street, Universe, Space', phone: '(555) 424-2424', isCurrentUser: false, accountType: 'Millionaire Account', isHighValue: true, isEasterEgg: true, easterEggType: 'millionaire', bonusPoints: 20, easterEggMessage: '💰 Millionaire Discovered! The answer to life, universe, and everything.' }, { id: 54, name: 'Jane Smith', email: 'jane.smith@example.com', accountBalance: '$45,890.50', accountNumber: '****5454', lastLogin: '2026-02-08 08:30', address: '456 Oak Avenue, Springfield, USA', phone: '(555) 234-5678', isCurrentUser: false, accountType: 'Premium Savings', securityLevel: 'high', isNeighbor: true, bonusPoints: 10 }, { id: 55, name: 'Max Mustermann', email: 'max.mustermann@example.com', accountBalance: '$2,340.75', accountNumber: '****5555', lastLogin: '2026-02-08 09:15', address: '123 Main Street, Anytown, USA', phone: '(555) 123-4567', isCurrentUser: true, accountType: 'Standard Checking' }, { id: 56, name: 'Lisa Wagner', email: 'lisa.w@example.com', accountBalance: '$18,250.00', accountNumber: '****5656', lastLogin: '2026-02-08 07:45', address: '789 Pine Road, Neighborhood, USA', phone: '(555) 567-8901', isCurrentUser: false, accountType: 'Business Checking', isNeighbor: true, bonusPoints: 10 }, { id: 67, name: '¯\\_(ツ)_/¯', email: 'mystery@example.com', accountBalance: '$6,700.00', accountNumber: '****6767', lastLogin: '2026-01-01 00:00', address: '¯\\_(ツ)_/¯', phone: '¯\\_(ツ)_/¯', isCurrentUser: false, accountType: 'Mystery Account', isEasterEgg: true, easterEggType: 'shrug', bonusPoints: 15, easterEggMessage: '¯\\_(ツ)_/¯' }, { id: 100, name: 'Diana Prince', email: 'diana.p@example.com', accountBalance: '$125,000.00', accountNumber: '****1000', lastLogin: '2026-02-08 07:00', address: '100 Hero Boulevard, Metro City, USA', phone: '(555) 100-0001', isCurrentUser: false, accountType: 'Premium Investment', securityLevel: 'maximum' } ]; } /** * Fetch user profile by ID (vulnerable simulation) * Called via executeLessonAction endpoint * @param {number} userId - User ID to fetch * @returns {Object} User profile data or error */ async fetchUserProfile(userId, participantId, eventLessonId) { const users = this.getMockUsers(); const requestedId = parseInt(userId); // Find user const user = users.find(u => u.id === requestedId); if (!user) { return { success: false, error: 'USER_NOT_FOUND', message: 'Benutzer nicht gefunden', statusCode: 404 }; } // Detect unauthorized access const isUnauthorized = !user.isCurrentUser; // Award points for discoveries let pointsAwarded = 0; let discoveryMessage = null; if (isUnauthorized && participantId && eventLessonId) { // Award points for any IDOR discovery pointsAwarded = 10; // Check for easter eggs and award bonus points if (user.isEasterEgg) { pointsAwarded += user.bonusPoints; discoveryMessage = user.easterEggMessage; } else if (user.isNeighbor) { pointsAwarded += user.bonusPoints; } // Award points (don't duplicate if same user accessed multiple times) try { await this.awardPoints(participantId, eventLessonId, pointsAwarded, `IDOR discovered: User ${requestedId}`); } catch (error) { console.error('Failed to award IDOR points:', error); } } return { success: true, user: { id: user.id, name: user.name, email: user.email, accountBalance: user.accountBalance, accountNumber: user.accountNumber, lastLogin: user.lastLogin, address: user.address, phone: user.phone, accountType: user.accountType, ...(user.isHighValue && { isHighValue: true }), ...(user.adminAccess && { adminAccess: true }), ...(user.securityLevel && { securityLevel: user.securityLevel }) }, isCurrentUser: user.isCurrentUser, isUnauthorized, pointsAwarded: pointsAwarded > 0 ? pointsAwarded : undefined, easterEgg: user.isEasterEgg ? { type: user.easterEggType, message: discoveryMessage } : undefined, vulnerability: isUnauthorized ? { type: 'IDOR', severity: user.isHighValue || user.adminAccess ? 'CRITICAL' : 'HIGH', description: '⚠️ IDOR-Schwachstelle entdeckt!', message: `Sie sehen ${user.name}s private Daten ohne Berechtigung!`, impact: 'Ein Angreifer kann auf sensible Informationen eines beliebigen Benutzers zugreifen, indem er einfach den userId-Parameter in der URL ändert.', recommendation: 'Implementieren Sie ordnungsgemäße Autorisierungsprüfungen. Überprüfen Sie, ob der authentifizierte Benutzer die Berechtigung hat, auf die angeforderte Ressource zuzugreifen.', cve: user.isHighValue ? 'Dies ist eine kritische Schwachstelle - Admin-/Hochwertkonto aufgerufen!' : null } : null }; } /** * Get interactive data for IDOR demo step * @param {string} stepId - Step identifier * @returns {Object} Interactive component data */ async getInteractiveData(stepId) { if (stepId === 'idor-demo') { return { baseUrl: 'https://securebank.example/profile', currentUserId: 55, vulnerableParameter: 'userId', easterEggs: [ { id: 1, type: 'admin', found: false }, { id: 42, type: 'millionaire', found: false }, { id: 67, type: 'shrug', found: false }, { id: 54, type: 'neighbor', found: false }, { id: 56, type: 'neighbor', found: false } ], secureApproach: { title: 'Sichere Implementierung', description: 'Anstelle von URL-Parametern, verwenden Sie sitzungsbasierte Authentifizierung', example: 'GET /profile (gibt nur die Daten des authentifizierten Benutzers zurück)', code: ` // Anfällig (IDOR): app.get('/profile', (req, res) => { const userId = req.query.userId; // ❌ Jeder kann dies ändern! const user = db.getUserById(userId); res.json(user); }); // Sicher (Sitzungsbasiert): app.get('/profile', authenticate, (req, res) => { const userId = req.session.userId; // ✅ Aus verifizierter Sitzung if (req.params.userId && req.params.userId !== userId) { return res.status(403).json({ error: 'Forbidden' }); } const user = db.getUserById(userId); res.json(user); }); `.trim() } }; } return await super.getInteractiveData(stepId); } } module.exports = IDORDemoLesson;