medienkompetenz-lernplattform/backend/lessons/modules/idor-demo/index.js
Marius Rometsch a439873394 Add lessons
2026-02-08 19:47:21 +01:00

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;