210 lines
7.6 KiB
JavaScript
210 lines
7.6 KiB
JavaScript
const LessonModule = require('../base/LessonModule');
|
|
|
|
class SQLInjectionShopLesson extends LessonModule {
|
|
constructor(config) {
|
|
super(config);
|
|
}
|
|
|
|
// Mock database with products
|
|
getMockDatabase() {
|
|
return {
|
|
products: [
|
|
{ id: 1, name: 'Laptop Pro 15', price: 1299.99, category: 'Electronics', stock: 15 },
|
|
{ id: 2, name: 'Wireless Mouse', price: 29.99, category: 'Accessories', stock: 50 },
|
|
{ id: 3, name: 'USB-C Cable', price: 12.99, category: 'Accessories', stock: 100 },
|
|
{ id: 4, name: 'Gaming Keyboard', price: 89.99, category: 'Electronics', stock: 25 },
|
|
{ id: 5, name: 'Monitor 27"', price: 349.99, category: 'Electronics', stock: 20 },
|
|
{ id: 6, name: 'Webcam HD', price: 79.99, category: 'Electronics', stock: 30 },
|
|
{ id: 7, name: 'Desk Lamp', price: 34.99, category: 'Office', stock: 40 },
|
|
{ id: 8, name: 'Notebook Set', price: 15.99, category: 'Office', stock: 60 }
|
|
],
|
|
users: [
|
|
{ id: 1, username: 'admin', password: 'hashed_admin_password', role: 'admin' },
|
|
{ id: 2, username: 'john_doe', password: 'hashed_user_password', role: 'customer' },
|
|
{ id: 3, username: 'jane_smith', password: 'hashed_user_password', role: 'customer' }
|
|
],
|
|
orders: [
|
|
{ id: 1, user_id: 2, total: 1329.98, status: 'shipped' },
|
|
{ id: 2, user_id: 3, total: 89.99, status: 'processing' }
|
|
]
|
|
};
|
|
}
|
|
|
|
// Simulate vulnerable SQL query
|
|
executeVulnerableQuery(searchTerm) {
|
|
const db = this.getMockDatabase();
|
|
|
|
// Build the "vulnerable" query string for educational display
|
|
const vulnerableQuery = `SELECT * FROM products WHERE name LIKE '%${searchTerm}%'`;
|
|
|
|
// Detect SQL injection attempts
|
|
const injectionDetected = this.detectInjection(searchTerm);
|
|
|
|
let results = [];
|
|
let injectionType = null;
|
|
let explanation = '';
|
|
|
|
if (injectionDetected) {
|
|
const injectionInfo = this.analyzeInjection(searchTerm);
|
|
injectionType = injectionInfo.type;
|
|
explanation = injectionInfo.explanation;
|
|
|
|
// Simulate different injection results
|
|
if (injectionInfo.type === 'OR_ALWAYS_TRUE') {
|
|
// Return all products (simulating OR '1'='1')
|
|
results = db.products;
|
|
} else if (injectionInfo.type === 'UNION_SELECT') {
|
|
// Simulate UNION attack showing user data
|
|
results = [
|
|
{ id: 'INJECTED', name: 'admin', price: 'hashed_admin_password', category: 'LEAKED DATA', stock: 'admin' },
|
|
{ id: 'INJECTED', name: 'john_doe', price: 'hashed_user_password', category: 'LEAKED DATA', stock: 'customer' },
|
|
{ id: 'INJECTED', name: 'jane_smith', price: 'hashed_user_password', category: 'LEAKED DATA', stock: 'customer' }
|
|
];
|
|
} else if (injectionInfo.type === 'DROP_TABLE') {
|
|
// Simulate destructive command
|
|
results = [];
|
|
explanation += ' In a real scenario, this could delete the entire products table!';
|
|
} else if (injectionInfo.type === 'COMMENT_INJECTION') {
|
|
// Bypass rest of query
|
|
results = db.products;
|
|
}
|
|
} else {
|
|
// Normal search - filter products by name
|
|
results = db.products.filter(p =>
|
|
p.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
);
|
|
}
|
|
|
|
return {
|
|
query: vulnerableQuery,
|
|
results,
|
|
injectionDetected,
|
|
injectionType,
|
|
explanation,
|
|
recordCount: results.length
|
|
};
|
|
}
|
|
|
|
// Detect if input contains SQL injection
|
|
detectInjection(input) {
|
|
const injectionPatterns = [
|
|
/'/, // Single quote
|
|
/--/, // SQL comment
|
|
/;/, // Statement separator
|
|
/union/i, // UNION keyword
|
|
/select/i, // SELECT keyword
|
|
/drop/i, // DROP keyword
|
|
/insert/i, // INSERT keyword
|
|
/update/i, // UPDATE keyword
|
|
/delete/i, // DELETE keyword
|
|
/or\s+['"]?\d+['"]?\s*=\s*['"]?\d+['"]?/i // OR 1=1 pattern
|
|
];
|
|
|
|
return injectionPatterns.some(pattern => pattern.test(input));
|
|
}
|
|
|
|
// Analyze the type of SQL injection
|
|
analyzeInjection(input) {
|
|
const lowerInput = input.toLowerCase();
|
|
|
|
if (lowerInput.includes('union') && lowerInput.includes('select')) {
|
|
return {
|
|
type: 'UNION_SELECT',
|
|
explanation: '⚠️ UNION SELECT injection detected! This technique combines results from multiple tables, potentially exposing sensitive data like usernames and passwords.'
|
|
};
|
|
}
|
|
|
|
if (lowerInput.includes('drop')) {
|
|
return {
|
|
type: 'DROP_TABLE',
|
|
explanation: '🚨 DROP TABLE injection detected! This is a destructive attack that could delete entire database tables. Critical data loss would occur!'
|
|
};
|
|
}
|
|
|
|
if (lowerInput.includes("'") && (lowerInput.includes('or') || lowerInput.includes('||'))) {
|
|
if (lowerInput.match(/or\s+['"]?\d+['"]?\s*=\s*['"]?\d+['"]?/)) {
|
|
return {
|
|
type: 'OR_ALWAYS_TRUE',
|
|
explanation: "⚠️ OR injection detected! The condition '1'='1' is always true, bypassing the intended filter and returning ALL records."
|
|
};
|
|
}
|
|
}
|
|
|
|
if (lowerInput.includes('--') || lowerInput.includes('#')) {
|
|
return {
|
|
type: 'COMMENT_INJECTION',
|
|
explanation: '⚠️ Comment injection detected! The -- sequence comments out the rest of the SQL query, potentially bypassing security checks.'
|
|
};
|
|
}
|
|
|
|
if (lowerInput.includes(';')) {
|
|
return {
|
|
type: 'MULTIPLE_STATEMENTS',
|
|
explanation: '⚠️ Multiple statement injection detected! The semicolon allows execution of additional SQL commands, enabling complex attacks.'
|
|
};
|
|
}
|
|
|
|
// Generic injection
|
|
return {
|
|
type: 'GENERIC',
|
|
explanation: '⚠️ SQL injection attempt detected! Special characters in the input could manipulate the query structure.'
|
|
};
|
|
}
|
|
|
|
// Demonstrate safe parameterized query
|
|
executeSafeQuery(searchTerm) {
|
|
const db = this.getMockDatabase();
|
|
|
|
// Show the safe query with placeholder
|
|
const safeQuery = `SELECT * FROM products WHERE name LIKE ?`;
|
|
const parameter = `%${searchTerm}%`;
|
|
|
|
// Execute safe search (treats all input as literal data)
|
|
const results = db.products.filter(p =>
|
|
p.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
);
|
|
|
|
return {
|
|
query: safeQuery,
|
|
parameter,
|
|
results,
|
|
explanation: '✅ Parameterized query used! User input is treated as data only, never as SQL code. Injection is impossible.',
|
|
recordCount: results.length
|
|
};
|
|
}
|
|
|
|
// Get interactive data for the SQL shop demo
|
|
getInteractiveData(stepId) {
|
|
if (stepId === 'shop-demo') {
|
|
return {
|
|
database: this.getMockDatabase(),
|
|
examples: [
|
|
{
|
|
label: 'Normal Search',
|
|
input: 'laptop',
|
|
description: 'Search for products containing "laptop"'
|
|
},
|
|
{
|
|
label: 'View All Products (OR injection)',
|
|
input: "' OR '1'='1",
|
|
description: 'Exploit: Returns all products by making condition always true'
|
|
},
|
|
{
|
|
label: 'Extract User Data (UNION)',
|
|
input: "' UNION SELECT id, username, password, role, 'LEAKED' FROM users--",
|
|
description: 'Exploit: Combines product results with user table data'
|
|
},
|
|
{
|
|
label: 'Destructive Attack (DROP)',
|
|
input: "'; DROP TABLE products--",
|
|
description: 'Exploit: Attempts to delete the products table'
|
|
}
|
|
]
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
module.exports = SQLInjectionShopLesson;
|