260 lines
8.2 KiB
JavaScript
260 lines
8.2 KiB
JavaScript
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;
|