Insecure Direct Object References (IDOR)

High Risk Authentication & Authorization
idoraccess-controlauthorizationobject-referencesprivilege-escalationdata-exposureenumerationpath-traversalresource-access

What it is

A critical access control vulnerability where applications expose direct references to internal objects (like database records, files, or resources) without proper authorization checks. Attackers can manipulate these references to access unauthorized data belonging to other users, bypass access controls, and potentially gain access to sensitive information or administrative functions.

// VULNERABLE: E-commerce system with multiple IDOR vulnerabilities const express = require('express'); const app = express(); app.use(express.json()); // VULNERABLE: Order management system class VulnerableOrderService { // VULNERABLE: Sequential order IDs static orderIdCounter = 10000; static async createOrder(customerId, items) { const order = { id: ++this.orderIdCounter, // Predictable IDs: 10001, 10002, etc. customerId: customerId, items: items, total: items.reduce((sum, item) => sum + (item.price * item.quantity), 0), status: 'pending', paymentDetails: { creditCard: '****-****-****-1234', billingAddress: '123 Secret St' }, shippingAddress: '456 Private Ave', internalNotes: 'High-value customer - priority shipping', createdAt: new Date() }; await Order.create(order); return order; } } // VULNERABLE: Order endpoints without authorization app.get('/orders/:orderId', async (req, res) => { const { orderId } = req.params; try { // VULNERABLE: No authorization check! // Anyone can access any order by guessing the ID const order = await Order.findById(parseInt(orderId)); if (!order) { return res.status(404).json({ error: 'Order not found' }); } // VULNERABLE: Exposing all order details including sensitive info res.json({ id: order.id, customerId: order.customerId, customerEmail: order.customerEmail, items: order.items, total: order.total, status: order.status, paymentDetails: order.paymentDetails, // Sensitive payment info! shippingAddress: order.shippingAddress, internalNotes: order.internalNotes, // Internal business notes! createdAt: order.createdAt }); } catch (error) { res.status(500).json({ error: 'Server error' }); } }); // VULNERABLE: Order modification without ownership check app.put('/orders/:orderId', async (req, res) => { const { orderId } = req.params; const { shippingAddress, items } = req.body; try { // VULNERABLE: No check if current user owns this order const order = await Order.findById(parseInt(orderId)); if (!order) { return res.status(404).json({ error: 'Order not found' }); } // VULNERABLE: Anyone can modify any order const updatedOrder = await Order.findByIdAndUpdate( parseInt(orderId), { shippingAddress: shippingAddress, items: items, // Recalculate total based on new items total: items.reduce((sum, item) => sum + (item.price * item.quantity), 0) }, { new: true } ); res.json({ message: 'Order updated successfully', order: updatedOrder }); } catch (error) { res.status(500).json({ error: 'Update failed' }); } }); // VULNERABLE: Order cancellation without proper authorization app.delete('/orders/:orderId', async (req, res) => { const { orderId } = req.params; try { // VULNERABLE: No ownership verification const order = await Order.findById(parseInt(orderId)); if (!order) { return res.status(404).json({ error: 'Order not found' }); } // VULNERABLE: Anyone can cancel any order await Order.findByIdAndUpdate( parseInt(orderId), { status: 'cancelled', cancelledAt: new Date(), cancelledBy: req.user?.id || 'anonymous' } ); res.json({ message: 'Order cancelled successfully' }); } catch (error) { res.status(500).json({ error: 'Cancellation failed' }); } }); // VULNERABLE: Invoice access with direct reference app.get('/invoice/:orderId', async (req, res) => { const { orderId } = req.params; try { // VULNERABLE: Direct order access without authorization const order = await Order.findById(parseInt(orderId)); if (!order) { return res.status(404).json({ error: 'Invoice not found' }); } // VULNERABLE: Exposing financial details to anyone const invoice = { invoiceNumber: `INV-${order.id}`, orderId: order.id, customerInfo: { id: order.customerId, email: order.customerEmail, name: order.customerName }, items: order.items, subtotal: order.subtotal, tax: order.tax, total: order.total, paymentMethod: order.paymentDetails.method, lastFourDigits: order.paymentDetails.lastFour, billingAddress: order.paymentDetails.billingAddress, shippingAddress: order.shippingAddress, // VULNERABLE: Internal financial data exposed costOfGoods: order.costOfGoods, profit: order.total - order.costOfGoods, supplierInfo: order.supplierDetails }; res.json(invoice); } catch (error) { res.status(500).json({ error: 'Server error' }); } }); // VULNERABLE: Order enumeration endpoint app.get('/orders', async (req, res) => { const { start = 10000, limit = 100 } = req.query; try { const startId = parseInt(start); const limitNum = Math.min(parseInt(limit), 1000); const orders = []; // VULNERABLE: Allows enumeration of all orders for (let i = startId; i < startId + limitNum; i++) { const order = await Order.findById(i); if (order) { // VULNERABLE: No authorization check orders.push({ id: order.id, customerId: order.customerId, total: order.total, status: order.status, customerEmail: order.customerEmail, createdAt: order.createdAt }); } } res.json({ orders: orders, startId: startId, count: orders.length, message: `Found ${orders.length} orders` }); } catch (error) { res.status(500).json({ error: 'Server error' }); } }); // VULNERABLE: Payment details access app.get('/payment/:orderId', async (req, res) => { const { orderId } = req.params; try { // VULNERABLE: Direct payment data access const order = await Order.findById(parseInt(orderId)); if (!order) { return res.status(404).json({ error: 'Payment not found' }); } // VULNERABLE: Exposing complete payment information res.json({ orderId: order.id, paymentDetails: { method: order.paymentDetails.method, creditCardNumber: order.paymentDetails.fullCardNumber, // Full CC number! expiryDate: order.paymentDetails.expiry, cvv: order.paymentDetails.cvv, // CVV exposed! billingAddress: order.paymentDetails.billingAddress, paymentProcessor: order.paymentDetails.processor, transactionId: order.paymentDetails.transactionId, merchantFees: order.paymentDetails.fees // Business data! }, refundHistory: order.refunds, chargebackRisk: order.riskAssessment }); } catch (error) { res.status(500).json({ error: 'Server error' }); } }); // VULNERABLE: Customer profile access through order app.get('/customer/:customerId/profile', async (req, res) => { const { customerId } = req.params; try { // VULNERABLE: Direct customer data access const customer = await Customer.findById(parseInt(customerId)); if (!customer) { return res.status(404).json({ error: 'Customer not found' }); } // VULNERABLE: Exposing complete customer profile res.json({ id: customer.id, personalInfo: { fullName: customer.fullName, email: customer.email, phone: customer.phone, dateOfBirth: customer.dateOfBirth, socialSecurityNumber: customer.ssn, // PII exposed! driversLicense: customer.license }, addresses: customer.addresses, paymentMethods: customer.paymentMethods, // All saved cards! orderHistory: customer.orderHistory, loyaltyPoints: customer.loyaltyPoints, creditScore: customer.creditScore, // Financial data! internalNotes: customer.customerServiceNotes, riskProfile: customer.riskAssessment }); } catch (error) { res.status(500).json({ error: 'Server error' }); } }); // VULNERABLE: Admin functions exposed through predictable endpoints app.get('/admin/order/:orderId/full-details', async (req, res) => { const { orderId } = req.params; try { // VULNERABLE: No admin authorization check! const order = await Order.findById(parseInt(orderId)) .populate('customerId') .populate('items.productId'); if (!order) { return res.status(404).json({ error: 'Order not found' }); } // VULNERABLE: Exposing all internal business data res.json({ order: order, financials: { revenue: order.total, costs: order.costOfGoods, profit: order.total - order.costOfGoods, margin: ((order.total - order.costOfGoods) / order.total) * 100 }, operations: { warehouse: order.fulfillmentWarehouse, pickingList: order.pickingInstructions, shippingCost: order.actualShippingCost, handlingTime: order.processingTime }, analytics: { customerLifetimeValue: order.customer.ltv, acquisitionCost: order.customer.acquisitionCost, profitability: order.customer.profitabilityScore } }); } catch (error) { res.status(500).json({ error: 'Server error' }); } }); // DEMONSTRATION: How to exploit these vulnerabilities function demonstrateIDORAttacks() { console.log('=== IDOR Attack Demonstrations ==='); // Attack 1: Order enumeration console.log('\n1. Order Enumeration Attack:'); console.log('GET /orders?start=10000&limit=1000'); console.log('- Retrieves 1000 orders starting from ID 10000'); console.log('- Exposes customer emails, totals, and order details'); // Attack 2: Sensitive data access console.log('\n2. Payment Information Access:'); console.log('GET /payment/10001'); console.log('GET /payment/10002'); console.log('- Exposes full credit card numbers and CVVs'); console.log('- Reveals billing addresses and transaction details'); // Attack 3: Customer data harvesting console.log('\n3. Customer Data Harvesting:'); console.log('for (let i = 1; i <= 10000; i++) {'); console.log(' fetch(`/customer/${i}/profile`)'); console.log('}'); console.log('- Systematically harvests all customer PII'); console.log('- Exposes SSNs, credit scores, and internal notes'); // Attack 4: Order manipulation console.log('\n4. Order Manipulation:'); console.log('PUT /orders/10001'); console.log('Body: { "items": [], "shippingAddress": "Attacker Address" }'); console.log('- Modifies other customers\' orders'); console.log('- Can redirect shipments or cancel orders'); // Attack 5: Business intelligence gathering console.log('\n5. Business Intelligence Theft:'); console.log('GET /admin/order/10001/full-details'); console.log('- Accesses internal financial data'); console.log('- Reveals profit margins and business operations'); console.log('\n=== Attack Impact ==='); console.log('- Complete customer database compromise'); console.log('- Financial fraud through payment data theft'); console.log('- Business espionage via internal data access'); console.log('- Order fraud and shipping manipulation'); console.log('- Regulatory violations (PCI DSS, GDPR, etc.)'); } if (require.main === module) { demonstrateIDORAttacks(); app.listen(3000, () => { console.log('\nVulnerable e-commerce API running on port 3000'); console.log('Try these IDOR attacks:'); console.log('- Browse orders: GET /orders?start=10000&limit=100'); console.log('- Access payments: GET /payment/{orderId}'); console.log('- View customers: GET /customer/{customerId}/profile'); console.log('- Modify orders: PUT /orders/{orderId}'); console.log('- Admin access: GET /admin/order/{orderId}/full-details'); }); }
// SECURE: E-commerce system with proper IDOR protections const express = require('express'); const { v4: uuidv4 } = require('uuid'); const crypto = require('crypto'); const rateLimit = require('express-rate-limit'); const app = express(); app.use(express.json()); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests from this IP' }); app.use(limiter); // SECURE: Order service with proper access controls class SecureOrderService { // SECURE: UUID-based order IDs static async createOrder(customerId, items) { const order = { id: uuidv4(), // Non-predictable UUID customerId: customerId, items: items.map(item => ({ productId: item.productId, quantity: item.quantity, price: item.price, name: item.name })), total: items.reduce((sum, item) => sum + (item.price * item.quantity), 0), status: 'pending', // SECURE: Sensitive data stored separately with encryption paymentReference: uuidv4(), // Reference to separate payment record shippingReference: uuidv4(), // Reference to separate shipping record createdAt: new Date(), updatedAt: new Date() }; await Order.create(order); return order; } // SECURE: Authorization check for order access static async canAccessOrder(userId, orderId, accessType = 'read') { try { const order = await Order.findById(orderId).select('customerId'); if (!order) { return false; } // Customer can access their own orders if (order.customerId === userId) { return true; } // Check if user has staff permissions const user = await User.findById(userId).select('role permissions'); if (!user) { return false; } // Staff can access orders based on their role const allowedRoles = ['admin', 'customer-service', 'fulfillment']; if (allowedRoles.includes(user.role)) { // Further check specific permissions return user.permissions.includes(`order:${accessType}`); } return false; } catch (error) { console.error('Order access check error:', error); return false; } } // SECURE: Get sanitized order data based on user role static async getOrderData(orderId, userId, userRole) { const order = await Order.findById(orderId); if (!order) { return null; } // Base order data const orderData = { id: order.id, items: order.items, total: order.total, status: order.status, createdAt: order.createdAt, updatedAt: order.updatedAt }; // Add data based on access level if (order.customerId === userId) { // Customer's own order - add shipping info const shippingInfo = await ShippingInfo.findById(order.shippingReference); orderData.shippingAddress = shippingInfo?.address; orderData.trackingNumber = shippingInfo?.trackingNumber; } if (['admin', 'customer-service'].includes(userRole)) { // Staff access - add customer reference (not full data) orderData.customerReference = order.customerId; orderData.paymentStatus = await this.getPaymentStatus(order.paymentReference); } if (userRole === 'admin') { // Admin access - add operational data orderData.internalNotes = order.internalNotes; orderData.fulfillmentStatus = order.fulfillmentStatus; } return orderData; } static async getPaymentStatus(paymentReference) { const payment = await Payment.findById(paymentReference).select('status method'); return payment ? { status: payment.status, method: payment.method } : null; } } // SECURE: Authentication middleware function requireAuth(req, res, next) { // Implement proper JWT or session-based authentication const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Authentication required' }); } try { // Verify token and extract user info // This is a simplified version - implement proper JWT verification const token = authHeader.substring(7); const user = verifyToken(token); // Implement this function req.user = user; next(); } catch (error) { return res.status(401).json({ error: 'Invalid token' }); } } // SECURE: Order access with authorization app.get('/orders/:orderId', requireAuth, async (req, res) => { const { orderId } = req.params; const userId = req.user.id; const userRole = req.user.role; try { // Validate UUID format if (!isValidUUID(orderId)) { return res.status(400).json({ error: 'Invalid order ID format' }); } // Check authorization const canAccess = await SecureOrderService.canAccessOrder(userId, orderId, 'read'); if (!canAccess) { // Log unauthorized access attempt console.warn('Unauthorized order access attempt:', { userId: userId, orderId: orderId, ip: req.ip, userAgent: req.get('User-Agent'), timestamp: new Date().toISOString() }); return res.status(403).json({ error: 'Access denied' }); } // Get order data with appropriate level of detail const orderData = await SecureOrderService.getOrderData(orderId, userId, userRole); if (!orderData) { return res.status(404).json({ error: 'Order not found' }); } // Log successful access console.info('Order accessed:', { userId: userId, orderId: orderId, userRole: userRole, timestamp: new Date().toISOString() }); res.json(orderData); } catch (error) { console.error('Order access error:', error); res.status(500).json({ error: 'Server error' }); } }); // SECURE: Order modification with proper authorization app.put('/orders/:orderId', requireAuth, async (req, res) => { const { orderId } = req.params; const { shippingAddress } = req.body; const userId = req.user.id; try { if (!isValidUUID(orderId)) { return res.status(400).json({ error: 'Invalid order ID format' }); } // Check authorization for modification const canModify = await SecureOrderService.canAccessOrder(userId, orderId, 'write'); if (!canModify) { return res.status(403).json({ error: 'Access denied' }); } const order = await Order.findById(orderId); if (!order) { return res.status(404).json({ error: 'Order not found' }); } // Check if order can be modified const modifiableStatuses = ['pending', 'confirmed']; if (!modifiableStatuses.includes(order.status)) { return res.status(400).json({ error: 'Order cannot be modified in current status' }); } // Validate and sanitize input const updates = {}; if (shippingAddress) { // Validate shipping address format if (!isValidAddress(shippingAddress)) { return res.status(400).json({ error: 'Invalid shipping address' }); } // Update shipping information separately await ShippingInfo.findByIdAndUpdate(order.shippingReference, { address: shippingAddress, updatedAt: new Date() }); updates.updatedAt = new Date(); } // Update order if there are changes if (Object.keys(updates).length > 0) { await Order.findByIdAndUpdate(orderId, updates); // Log modification console.info('Order modified:', { userId: userId, orderId: orderId, changes: Object.keys(updates), timestamp: new Date().toISOString() }); } res.json({ message: 'Order updated successfully' }); } catch (error) { console.error('Order update error:', error); res.status(500).json({ error: 'Update failed' }); } }); // SECURE: Customer's order list with pagination app.get('/my-orders', requireAuth, async (req, res) => { const userId = req.user.id; const { page = 1, limit = 20, status } = req.query; try { const pageNum = Math.max(1, parseInt(page)); const limitNum = Math.min(50, Math.max(1, parseInt(limit))); // Build query for user's orders only const query = { customerId: userId }; if (status) { query.status = status; } const orders = await Order.find(query) .select('id items total status createdAt updatedAt') .sort({ createdAt: -1 }) .skip((pageNum - 1) * limitNum) .limit(limitNum); const totalOrders = await Order.countDocuments(query); res.json({ orders: orders, pagination: { page: pageNum, limit: limitNum, total: totalOrders, pages: Math.ceil(totalOrders / limitNum) } }); } catch (error) { console.error('Order list error:', error); res.status(500).json({ error: 'Failed to fetch orders' }); } }); // SECURE: Invoice access with token-based authorization app.get('/invoice/:orderId/access', requireAuth, async (req, res) => { const { orderId } = req.params; const userId = req.user.id; try { if (!isValidUUID(orderId)) { return res.status(400).json({ error: 'Invalid order ID format' }); } // Check order access const canAccess = await SecureOrderService.canAccessOrder(userId, orderId, 'read'); if (!canAccess) { return res.status(403).json({ error: 'Access denied' }); } // Generate time-limited invoice access token const tokenData = { orderId: orderId, userId: userId, type: 'invoice', expiresAt: Date.now() + (10 * 60 * 1000) // 10 minutes }; const token = crypto.createHmac('sha256', process.env.INVOICE_SECRET) .update(JSON.stringify(tokenData)) .digest('hex'); res.json({ invoiceToken: token, tokenData: Buffer.from(JSON.stringify(tokenData)).toString('base64'), expiresIn: 600 // 10 minutes }); } catch (error) { console.error('Invoice access error:', error); res.status(500).json({ error: 'Server error' }); } }); // SECURE: Invoice content with token validation app.get('/invoice/:orderId', async (req, res) => { const { orderId } = req.params; const { token, data } = req.query; try { // Validate access token const tokenData = JSON.parse(Buffer.from(data, 'base64').toString()); // Check token expiration if (Date.now() > tokenData.expiresAt) { return res.status(403).json({ error: 'Access token expired' }); } // Verify token signature const expectedToken = crypto.createHmac('sha256', process.env.INVOICE_SECRET) .update(JSON.stringify(tokenData)) .digest('hex'); if (token !== expectedToken || tokenData.orderId !== orderId) { return res.status(403).json({ error: 'Invalid access token' }); } // Get order and generate invoice const order = await Order.findById(orderId); if (!order) { return res.status(404).json({ error: 'Order not found' }); } // Get payment info (limited data) const payment = await Payment.findById(order.paymentReference) .select('method lastFour status'); const invoice = { invoiceNumber: `INV-${orderId.substring(0, 8).toUpperCase()}`, orderId: order.id, items: order.items, subtotal: order.subtotal, tax: order.tax, total: order.total, paymentMethod: payment?.method, lastFourDigits: payment?.lastFour, invoiceDate: order.createdAt, status: order.status }; // Log invoice access console.info('Invoice accessed:', { orderId: orderId, userId: tokenData.userId, timestamp: new Date().toISOString() }); res.json(invoice); } catch (error) { console.error('Invoice generation error:', error); res.status(500).json({ error: 'Server error' }); } }); // SECURE: Admin order management with role-based access app.get('/admin/orders', requireAuth, async (req, res) => { const userRole = req.user.role; const userId = req.user.id; // Check admin permissions if (!['admin', 'customer-service'].includes(userRole)) { return res.status(403).json({ error: 'Admin access required' }); } const { page = 1, limit = 50, search, status } = req.query; try { const pageNum = Math.max(1, parseInt(page)); const limitNum = Math.min(100, Math.max(1, parseInt(limit))); // Build search query let query = {}; if (status) { query.status = status; } if (search) { // Search by order ID or customer reference query.$or = [ { id: { $regex: search, $options: 'i' } }, { customerReference: { $regex: search, $options: 'i' } } ]; } const orders = await Order.find(query) .select('id customerId total status createdAt updatedAt') .sort({ createdAt: -1 }) .skip((pageNum - 1) * limitNum) .limit(limitNum); const totalOrders = await Order.countDocuments(query); // Log admin access console.info('Admin orders accessed:', { adminId: userId, role: userRole, search: search || 'none', timestamp: new Date().toISOString() }); res.json({ orders: orders, pagination: { page: pageNum, limit: limitNum, total: totalOrders, pages: Math.ceil(totalOrders / limitNum) } }); } catch (error) { console.error('Admin orders error:', error); res.status(500).json({ error: 'Server error' }); } }); // Helper functions function isValidUUID(uuid) { const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; return uuidRegex.test(uuid); } function isValidAddress(address) { return address && typeof address === 'object' && address.street && address.city && address.postalCode && address.country; } function verifyToken(token) { // Implement proper JWT verification // This is a placeholder try { const decoded = jwt.verify(token, process.env.JWT_SECRET); return decoded; } catch (error) { throw new Error('Invalid token'); } } // Error handling middleware app.use((error, req, res, next) => { console.error('Unhandled error:', { error: error.message, stack: error.stack, url: req.originalUrl, method: req.method, user: req.user?.id, timestamp: new Date().toISOString() }); res.status(500).json({ error: 'Internal server error' }); }); app.listen(3000, () => { console.log('Secure e-commerce API running on port 3000'); console.log('IDOR protections enabled:'); console.log('- UUID-based order identifiers'); console.log('- Comprehensive authorization checks'); console.log('- Role-based access control'); console.log('- Token-based sensitive data access'); console.log('- Separate storage for sensitive information'); console.log('- Comprehensive audit logging'); });

💡 Why This Fix Works

The vulnerable version demonstrates a complete IDOR vulnerability in an e-commerce system with predictable sequential IDs, missing authorization checks, and direct exposure of sensitive data including payment information and customer PII. The secure version implements UUID-based identifiers, comprehensive authorization checks, role-based access control, token-based access for sensitive operations, separate storage for sensitive data, and proper audit logging to prevent all forms of IDOR attacks.

Why it happens

Applications often expose database IDs, file paths, or other object references directly in URLs or API endpoints without verifying that the authenticated user has permission to access the referenced resource. This allows attackers to simply modify the reference to access other users' data.

Root causes

Missing Authorization Checks on Resource Access

Applications often expose database IDs, file paths, or other object references directly in URLs or API endpoints without verifying that the authenticated user has permission to access the referenced resource. This allows attackers to simply modify the reference to access other users' data.

Preview example – JAVASCRIPT
// VULNERABLE: Direct database ID exposure without authorization
const express = require('express');
const app = express();

// VULNERABLE: User profile endpoint
app.get('/user/:userId', async (req, res) => {
    const { userId } = req.params;
    
    try {
        // VULNERABLE: No authorization check!
        // Any authenticated user can access any other user's profile
        const user = await User.findById(userId);
        
        if (!user) {
            return res.status(404).json({ error: 'User not found' });
        }
        
        // Exposing all user data without permission check
        res.json({
            id: user.id,
            username: user.username,
            email: user.email,
            personalInfo: user.personalInfo,
            socialSecurityNumber: user.ssn,  // Sensitive data exposed!
            bankAccount: user.bankAccount,
            address: user.address
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Document access
app.get('/document/:docId', async (req, res) => {
    const { docId } = req.params;
    
    try {
        // VULNERABLE: No ownership verification
        const document = await Document.findById(docId);
        
        if (!document) {
            return res.status(404).json({ error: 'Document not found' });
        }
        
        // Anyone can access any document
        res.json({
            id: document.id,
            title: document.title,
            content: document.content,
            owner: document.userId,
            confidential: document.confidential
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Order management
app.get('/order/:orderId', async (req, res) => {
    const { orderId } = req.params;
    
    try {
        // VULNERABLE: Sequential order IDs make enumeration easy
        const order = await Order.findById(orderId);
        
        if (!order) {
            return res.status(404).json({ error: 'Order not found' });
        }
        
        // No check if current user owns this order
        res.json({
            id: order.id,
            items: order.items,
            total: order.total,
            customerEmail: order.customerEmail,
            paymentDetails: order.paymentDetails,  // Financial data exposed!
            shippingAddress: order.shippingAddress
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Admin functions exposed through predictable IDs
app.get('/admin/user/:userId/permissions', async (req, res) => {
    const { userId } = req.params;
    
    // VULNERABLE: No admin permission check!
    // Any user can access admin endpoints
    
    try {
        const permissions = await UserPermissions.findByUserId(userId);
        
        res.json({
            userId: userId,
            permissions: permissions,
            adminRights: permissions.adminRights,
            systemAccess: permissions.systemAccess
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: File access with direct path references
app.get('/file/:fileName', (req, res) => {
    const { fileName } = req.params;
    
    // VULNERABLE: Direct file access without authorization
    const filePath = `/uploads/${fileName}`;
    
    // No check if user owns the file or has permission
    res.sendFile(filePath, (err) => {
        if (err) {
            res.status(404).json({ error: 'File not found' });
        }
    });
});

// VULNERABLE: Delete operations without authorization
app.delete('/post/:postId', async (req, res) => {
    const { postId } = req.params;
    
    try {
        // VULNERABLE: Any user can delete any post
        const result = await Post.findByIdAndDelete(postId);
        
        if (!result) {
            return res.status(404).json({ error: 'Post not found' });
        }
        
        res.json({ message: 'Post deleted successfully' });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

Predictable Object Identifiers and Enumeration

Using sequential, predictable, or easily guessable identifiers makes it trivial for attackers to enumerate and access other users' resources. Combined with missing authorization checks, this leads to complete data exposure through systematic enumeration attacks.

Preview example – JAVASCRIPT
// VULNERABLE: Predictable identifiers enabling enumeration attacks
const express = require('express');
const app = express();

// VULNERABLE: Sequential user IDs
let userIdCounter = 1;

class VulnerableUserService {
    static async createUser(userData) {
        // VULNERABLE: Predictable sequential IDs
        const newUser = {
            id: userIdCounter++,  // 1, 2, 3, 4...
            username: userData.username,
            email: userData.email,
            role: userData.role || 'user',
            createdAt: new Date()
        };
        
        // Save to database
        await User.create(newUser);
        return newUser;
    }
    
    static async getUserProfile(userId) {
        // VULNERABLE: No authorization check
        return await User.findById(userId);
    }
}

// VULNERABLE: API endpoints that enable enumeration
app.get('/api/users/:id', async (req, res) => {
    const userId = parseInt(req.params.id);
    
    try {
        // VULNERABLE: Attackers can iterate through IDs 1, 2, 3...
        const user = await VulnerableUserService.getUserProfile(userId);
        
        if (!user) {
            // VULNERABLE: Different responses leak information
            return res.status(404).json({ error: 'User not found' });
        }
        
        // Exposing user data without permission check
        res.json({
            id: user.id,
            username: user.username,
            email: user.email,
            role: user.role,
            lastLogin: user.lastLogin,
            isActive: user.isActive
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Invoice enumeration
app.get('/invoice/:invoiceNumber', async (req, res) => {
    const invoiceNumber = parseInt(req.params.invoiceNumber);
    
    try {
        // VULNERABLE: Sequential invoice numbers (1001, 1002, 1003...)
        const invoice = await Invoice.findByNumber(invoiceNumber);
        
        if (!invoice) {
            return res.status(404).json({ error: 'Invoice not found' });
        }
        
        // VULNERABLE: No ownership verification
        res.json({
            number: invoice.number,
            amount: invoice.amount,
            customerName: invoice.customerName,
            items: invoice.items,
            paymentStatus: invoice.paymentStatus,
            confidentialNotes: invoice.internalNotes  // Internal data exposed!
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Systematic enumeration endpoint
app.get('/api/enumerate/:resourceType/:id', async (req, res) => {
    const { resourceType, id } = req.params;
    const resourceId = parseInt(id);
    
    try {
        let resource;
        
        // VULNERABLE: Generic enumeration across different resource types
        switch (resourceType) {
            case 'users':
                resource = await User.findById(resourceId);
                break;
            case 'orders':
                resource = await Order.findById(resourceId);
                break;
            case 'documents':
                resource = await Document.findById(resourceId);
                break;
            case 'payments':
                resource = await Payment.findById(resourceId);
                break;
            default:
                return res.status(400).json({ error: 'Invalid resource type' });
        }
        
        if (!resource) {
            return res.status(404).json({ error: `${resourceType} not found` });
        }
        
        // VULNERABLE: No authorization check for any resource type
        res.json(resource);
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Batch enumeration endpoint
app.get('/api/batch/:resourceType', async (req, res) => {
    const { resourceType } = req.params;
    const { start = 1, limit = 100 } = req.query;
    
    try {
        const startId = parseInt(start);
        const limitNum = Math.min(parseInt(limit), 1000);  // Cap at 1000
        
        const results = [];
        
        // VULNERABLE: Allows bulk enumeration of resources
        for (let i = startId; i < startId + limitNum; i++) {
            let resource;
            
            switch (resourceType) {
                case 'users':
                    resource = await User.findById(i);
                    break;
                case 'orders':
                    resource = await Order.findById(i);
                    break;
                case 'documents':
                    resource = await Document.findById(i);
                    break;
            }
            
            if (resource) {
                // VULNERABLE: No authorization check
                results.push(resource);
            }
        }
        
        res.json({
            resourceType,
            startId: startId,
            count: results.length,
            data: results  // Massive data exposure!
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: File enumeration through predictable naming
app.get('/download/:fileId', async (req, res) => {
    const fileId = parseInt(req.params.fileId);
    
    try {
        // VULNERABLE: Sequential file IDs
        const file = await File.findById(fileId);
        
        if (!file) {
            return res.status(404).json({ error: 'File not found' });
        }
        
        // VULNERABLE: No ownership check
        const filePath = `/uploads/${file.filename}`;
        
        res.download(filePath, file.originalName, (err) => {
            if (err) {
                res.status(500).json({ error: 'Download failed' });
            }
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Administrative enumeration
app.get('/admin/audit/:userId', async (req, res) => {
    const userId = parseInt(req.params.userId);
    
    try {
        // VULNERABLE: No admin permission check!
        // VULNERABLE: Predictable user IDs allow full user enumeration
        const auditLog = await AuditLog.findByUserId(userId);
        
        res.json({
            userId: userId,
            loginHistory: auditLog.logins,
            actionHistory: auditLog.actions,
            permissions: auditLog.permissions,
            sensitiveOperations: auditLog.sensitiveOps,
            ipAddresses: auditLog.ipHistory
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

Client-Side Authorization Reliance and Parameter Manipulation

Applications that rely on client-side authorization checks or expose sensitive object references in client-side code are vulnerable to manipulation. Attackers can modify parameters, hidden form fields, or client-side data to access unauthorized resources.

Preview example – JAVASCRIPT
// VULNERABLE: Client-side authorization and parameter manipulation
const express = require('express');
const app = express();

// VULNERABLE: Hidden form fields with sensitive IDs
app.get('/account-settings', async (req, res) => {
    const currentUser = req.user;  // Assume authentication middleware
    
    // VULNERABLE: Embedding user ID in form
    const htmlForm = `
        <form action="/update-profile" method="POST">
            <!-- VULNERABLE: Hidden field can be manipulated -->
            <input type="hidden" name="userId" value="${currentUser.id}">
            <input type="hidden" name="accountType" value="${currentUser.accountType}">
            
            <label>Name:</label>
            <input type="text" name="name" value="${currentUser.name}">
            
            <label>Email:</label>
            <input type="text" name="email" value="${currentUser.email}">
            
            <!-- VULNERABLE: Admin privilege flag in client -->
            <input type="hidden" name="isAdmin" value="${currentUser.isAdmin}">
            
            <button type="submit">Update Profile</button>
        </form>
    `;
    
    res.send(htmlForm);
});

// VULNERABLE: Trusting client-provided parameters
app.post('/update-profile', async (req, res) => {
    const { userId, accountType, name, email, isAdmin } = req.body;
    
    try {
        // VULNERABLE: Trusting userId from client
        // Attacker can modify hidden field to update other users
        const user = await User.findById(userId);
        
        if (!user) {
            return res.status(404).json({ error: 'User not found' });
        }
        
        // VULNERABLE: No verification that current user can modify this profile
        // VULNERABLE: Trusting isAdmin flag from client
        await User.findByIdAndUpdate(userId, {
            name: name,
            email: email,
            accountType: accountType,
            isAdmin: isAdmin === 'true'  // Privilege escalation!
        });
        
        res.json({ message: 'Profile updated successfully' });
        
    } catch (error) {
        res.status(500).json({ error: 'Update failed' });
    }
});

// VULNERABLE: Client-side role checks
app.get('/dashboard', async (req, res) => {
    const currentUser = req.user;
    
    // VULNERABLE: Sending sensitive data to client for filtering
    const allUsers = await User.find({});
    const allOrders = await Order.find({});
    const allReports = await Report.find({});
    
    // Client will filter based on user role - INSECURE!
    res.json({
        currentUser: currentUser,
        users: allUsers,  // All user data sent to client!
        orders: allOrders,  // All order data sent to client!
        reports: allReports,  // All reports sent to client!
        
        // VULNERABLE: Client-side authorization data
        permissions: {
            canViewUsers: currentUser.role === 'admin',
            canEditOrders: currentUser.role === 'admin' || currentUser.role === 'manager',
            canViewReports: currentUser.role !== 'guest'
        }
    });
});

// VULNERABLE: Query parameter manipulation
app.get('/api/data', async (req, res) => {
    const { 
        userId = req.user.id,  // Default to current user
        includeDeleted = false,
        showSensitive = false 
    } = req.query;
    
    try {
        // VULNERABLE: Trusting query parameters for authorization
        let query = { userId: userId };
        
        // VULNERABLE: Client can set includeDeleted=true
        if (includeDeleted === 'true') {
            delete query.deleted;  // Show deleted records
        } else {
            query.deleted = { $ne: true };
        }
        
        const userData = await UserData.find(query);
        
        // VULNERABLE: Client can set showSensitive=true
        const response = userData.map(data => {
            const result = {
                id: data.id,
                content: data.content,
                createdAt: data.createdAt
            };
            
            // Client controls whether sensitive data is included
            if (showSensitive === 'true') {
                result.socialSecurityNumber = data.ssn;
                result.creditCardNumber = data.creditCard;
                result.passwordHash = data.passwordHash;
            }
            
            return result;
        });
        
        res.json(response);
        
    } catch (error) {
        res.status(500).json({ error: 'Server error' });
    }
});

// VULNERABLE: Cookie-based authorization
app.get('/admin/users', async (req, res) => {
    // VULNERABLE: Trusting client-side cookie for authorization
    const userRole = req.cookies.userRole;
    const isAdmin = req.cookies.isAdmin === 'true';
    
    // Client can modify cookies to gain admin access
    if (!isAdmin || userRole !== 'admin') {
        return res.status(403).json({ error: 'Admin access required' });
    }
    
    // VULNERABLE: If cookies are manipulated, this exposes all user data
    const allUsers = await User.find({}).select('+passwordHash +ssn +creditCard');
    
    res.json({
        users: allUsers,
        message: 'Admin access granted based on client cookies'
    });
});

// VULNERABLE: JavaScript-based authorization
app.get('/client-script', (req, res) => {
    const currentUser = req.user;
    
    // VULNERABLE: Authorization logic in client-side JavaScript
    const clientScript = `
        <script>
            const currentUser = ${JSON.stringify(currentUser)};
            
            // VULNERABLE: Client-side permission checks
            function checkPermission(action) {
                switch(action) {
                    case 'viewUsers':
                        return currentUser.role === 'admin';
                    case 'editProfile':
                        return true;  // Anyone can edit
                    case 'deleteUser':
                        return currentUser.role === 'admin';
                    default:
                        return false;
                }
            }
            
            // VULNERABLE: Sensitive data in client
            const sensitiveEndpoints = {
                adminPanel: '/admin/panel',
                userManagement: '/admin/users',
                systemSettings: '/admin/settings',
                // Exposing internal API endpoints
                internalApi: '/internal/api/users/{userId}'
            };
            
            function makeApiCall(endpoint, userId) {
                // VULNERABLE: Client constructs API calls with user IDs
                const url = endpoint.replace('{userId}', userId);
                
                // Client can modify userId to access other users
                fetch(url)
                    .then(response => response.json())
                    .then(data => console.log(data));
            }
            
            // VULNERABLE: Exposing all user IDs to client
            const allUserIds = ${JSON.stringify(await User.find({}).select('id'))};
        </script>
    `;
    
    res.send(clientScript);
});

// VULNERABLE: Session-based parameter injection
app.post('/transfer-funds', async (req, res) => {
    const { fromAccount, toAccount, amount } = req.body;
    
    try {
        // VULNERABLE: Trusting fromAccount from client
        // Attacker can specify any account as source
        const sourceAccount = await Account.findById(fromAccount);
        const targetAccount = await Account.findById(toAccount);
        
        if (!sourceAccount || !targetAccount) {
            return res.status(404).json({ error: 'Account not found' });
        }
        
        // VULNERABLE: No verification that current user owns fromAccount
        if (sourceAccount.balance < amount) {
            return res.status(400).json({ error: 'Insufficient funds' });
        }
        
        // Transfer funds without proper authorization
        await Account.findByIdAndUpdate(fromAccount, {
            $inc: { balance: -amount }
        });
        
        await Account.findByIdAndUpdate(toAccount, {
            $inc: { balance: amount }
        });
        
        res.json({ message: 'Transfer completed' });
        
    } catch (error) {
        res.status(500).json({ error: 'Transfer failed' });
    }
});

Fixes

1

Implement Proper Authorization Checks and Access Control

Always verify that the authenticated user has permission to access the requested resource. Implement consistent authorization checks at the server side for every resource access, never relying on client-side validation or parameters.

View implementation – JAVASCRIPT
// SECURE: Proper authorization checks and access control
const express = require('express');
const crypto = require('crypto');
const app = express();

// SECURE: Authorization middleware
class AuthorizationService {
    static async canAccessResource(userId, resourceType, resourceId) {
        try {
            switch (resourceType) {
                case 'user':
                    return await this.canAccessUser(userId, resourceId);
                case 'document':
                    return await this.canAccessDocument(userId, resourceId);
                case 'order':
                    return await this.canAccessOrder(userId, resourceId);
                case 'file':
                    return await this.canAccessFile(userId, resourceId);
                default:
                    return false;
            }
        } catch (error) {
            console.error('Authorization check failed:', error);
            return false;
        }
    }
    
    static async canAccessUser(currentUserId, targetUserId) {
        // Users can only access their own profile
        if (currentUserId === targetUserId) {
            return true;
        }
        
        // Check if current user is admin
        const currentUser = await User.findById(currentUserId);
        return currentUser && currentUser.role === 'admin';
    }
    
    static async canAccessDocument(userId, documentId) {
        const document = await Document.findById(documentId);
        if (!document) {
            return false;
        }
        
        // Check ownership
        if (document.ownerId === userId) {
            return true;
        }
        
        // Check shared permissions
        const permission = await DocumentPermission.findOne({
            documentId: documentId,
            userId: userId,
            isActive: true
        });
        
        return !!permission;
    }
    
    static async canAccessOrder(userId, orderId) {
        const order = await Order.findById(orderId);
        if (!order) {
            return false;
        }
        
        // Customer can access their own orders
        if (order.customerId === userId) {
            return true;
        }
        
        // Staff can access orders based on permissions
        const user = await User.findById(userId);
        const allowedRoles = ['admin', 'staff', 'manager'];
        
        return user && allowedRoles.includes(user.role);
    }
    
    static async canAccessFile(userId, fileId) {
        const file = await File.findById(fileId);
        if (!file) {
            return false;
        }
        
        return file.ownerId === userId;
    }
    
    static async hasRole(userId, requiredRoles) {
        const user = await User.findById(userId);
        if (!user) {
            return false;
        }
        
        if (Array.isArray(requiredRoles)) {
            return requiredRoles.includes(user.role);
        }
        
        return user.role === requiredRoles;
    }
}

// SECURE: Authorization middleware factory
function requireAuthorization(resourceType) {
    return async (req, res, next) => {
        const currentUserId = req.user?.id;
        const resourceId = req.params.id || req.params.userId || req.params.docId;
        
        if (!currentUserId) {
            return res.status(401).json({ 
                error: 'Authentication required' 
            });
        }
        
        try {
            const hasAccess = await AuthorizationService.canAccessResource(
                currentUserId, 
                resourceType, 
                resourceId
            );
            
            if (!hasAccess) {
                // Log unauthorized access attempt
                console.warn('Unauthorized access attempt:', {
                    userId: currentUserId,
                    resourceType,
                    resourceId,
                    timestamp: new Date().toISOString(),
                    userAgent: req.get('User-Agent'),
                    ip: req.ip
                });
                
                return res.status(403).json({ 
                    error: 'Access denied' 
                });
            }
            
            next();
            
        } catch (error) {
            console.error('Authorization middleware error:', error);
            return res.status(500).json({ 
                error: 'Authorization check failed' 
            });
        }
    };
}

// SECURE: Role-based authorization middleware
function requireRole(roles) {
    return async (req, res, next) => {
        const currentUserId = req.user?.id;
        
        if (!currentUserId) {
            return res.status(401).json({ 
                error: 'Authentication required' 
            });
        }
        
        try {
            const hasRole = await AuthorizationService.hasRole(currentUserId, roles);
            
            if (!hasRole) {
                console.warn('Role-based access denied:', {
                    userId: currentUserId,
                    requiredRoles: roles,
                    timestamp: new Date().toISOString()
                });
                
                return res.status(403).json({ 
                    error: 'Insufficient permissions' 
                });
            }
            
            next();
            
        } catch (error) {
            console.error('Role authorization error:', error);
            return res.status(500).json({ 
                error: 'Permission check failed' 
            });
        }
    };
}

// SECURE: User profile endpoint with proper authorization
app.get('/user/:id', requireAuthorization('user'), async (req, res) => {
    const { id } = req.params;
    const currentUserId = req.user.id;
    
    try {
        const user = await User.findById(id);
        
        if (!user) {
            return res.status(404).json({ error: 'User not found' });
        }
        
        // Return different data based on access level
        let userData;
        
        if (id === currentUserId) {
            // Own profile - return full data
            userData = {
                id: user.id,
                username: user.username,
                email: user.email,
                personalInfo: user.personalInfo,
                preferences: user.preferences,
                createdAt: user.createdAt
            };
        } else {
            // Admin accessing other user - return limited admin view
            userData = {
                id: user.id,
                username: user.username,
                email: user.email,
                role: user.role,
                isActive: user.isActive,
                lastLogin: user.lastLogin
            };
        }
        
        res.json(userData);
        
    } catch (error) {
        console.error('Error fetching user:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: Document access with proper ownership verification
app.get('/document/:id', requireAuthorization('document'), async (req, res) => {
    const { id } = req.params;
    const currentUserId = req.user.id;
    
    try {
        const document = await Document.findById(id);
        
        if (!document) {
            return res.status(404).json({ error: 'Document not found' });
        }
        
        // Get user's permission level for this document
        let permissionLevel = 'none';
        
        if (document.ownerId === currentUserId) {
            permissionLevel = 'owner';
        } else {
            const permission = await DocumentPermission.findOne({
                documentId: id,
                userId: currentUserId,
                isActive: true
            });
            
            if (permission) {
                permissionLevel = permission.level; // 'read', 'write', 'admin'
            }
        }
        
        // Return data based on permission level
        const response = {
            id: document.id,
            title: document.title,
            createdAt: document.createdAt,
            permissionLevel: permissionLevel
        };
        
        if (['read', 'write', 'admin', 'owner'].includes(permissionLevel)) {
            response.content = document.content;
        }
        
        if (['write', 'admin', 'owner'].includes(permissionLevel)) {
            response.metadata = document.metadata;
        }
        
        if (['admin', 'owner'].includes(permissionLevel)) {
            response.permissions = await DocumentPermission.find({ 
                documentId: id 
            }).populate('userId', 'username email');
        }
        
        res.json(response);
        
    } catch (error) {
        console.error('Error fetching document:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: Order access with proper customer verification
app.get('/order/:id', requireAuthorization('order'), async (req, res) => {
    const { id } = req.params;
    const currentUserId = req.user.id;
    
    try {
        const order = await Order.findById(id);
        
        if (!order) {
            return res.status(404).json({ error: 'Order not found' });
        }
        
        const currentUser = await User.findById(currentUserId);
        
        // Determine what data to return based on user role
        let orderData;
        
        if (order.customerId === currentUserId) {
            // Customer's own order
            orderData = {
                id: order.id,
                items: order.items,
                total: order.total,
                status: order.status,
                createdAt: order.createdAt,
                shippingAddress: order.shippingAddress,
                estimatedDelivery: order.estimatedDelivery
            };
        } else if (['admin', 'staff'].includes(currentUser.role)) {
            // Staff/admin view
            orderData = {
                id: order.id,
                customerId: order.customerId,
                customerEmail: order.customerEmail,
                items: order.items,
                total: order.total,
                status: order.status,
                paymentStatus: order.paymentStatus,
                createdAt: order.createdAt,
                shippingAddress: order.shippingAddress,
                internalNotes: order.internalNotes
            };
        }
        
        res.json(orderData);
        
    } catch (error) {
        console.error('Error fetching order:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: File access with proper ownership checks
app.get('/file/:id', requireAuthorization('file'), async (req, res) => {
    const { id } = req.params;
    const currentUserId = req.user.id;
    
    try {
        const file = await File.findById(id);
        
        if (!file) {
            return res.status(404).json({ error: 'File not found' });
        }
        
        // Verify file ownership or permissions
        if (file.ownerId !== currentUserId) {
            const permission = await FilePermission.findOne({
                fileId: id,
                userId: currentUserId,
                isActive: true
            });
            
            if (!permission) {
                return res.status(403).json({ error: 'Access denied' });
            }
        }
        
        // Log file access for audit
        await FileAccessLog.create({
            fileId: id,
            userId: currentUserId,
            action: 'download',
            timestamp: new Date(),
            ipAddress: req.ip,
            userAgent: req.get('User-Agent')
        });
        
        const filePath = path.join('/secure/uploads', file.filename);
        
        res.download(filePath, file.originalName, (err) => {
            if (err) {
                console.error('File download error:', err);
                if (!res.headersSent) {
                    res.status(500).json({ error: 'Download failed' });
                }
            }
        });
        
    } catch (error) {
        console.error('Error accessing file:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: Admin endpoints with role verification
app.get('/admin/users', requireRole(['admin', 'superadmin']), async (req, res) => {
    const currentUserId = req.user.id;
    const { page = 1, limit = 50 } = req.query;
    
    try {
        const pageNum = Math.max(1, parseInt(page));
        const limitNum = Math.min(100, Math.max(1, parseInt(limit)));
        const skip = (pageNum - 1) * limitNum;
        
        // Only return necessary fields for admin view
        const users = await User.find({})
            .select('username email role isActive lastLogin createdAt')
            .skip(skip)
            .limit(limitNum)
            .sort({ createdAt: -1 });
        
        const totalUsers = await User.countDocuments({});
        
        // Log admin access
        console.info('Admin user list accessed:', {
            adminId: currentUserId,
            timestamp: new Date().toISOString(),
            page: pageNum,
            limit: limitNum
        });
        
        res.json({
            users: users,
            pagination: {
                page: pageNum,
                limit: limitNum,
                total: totalUsers,
                pages: Math.ceil(totalUsers / limitNum)
            }
        });
        
    } catch (error) {
        console.error('Error fetching users for admin:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: Update profile with proper authorization
app.post('/update-profile', async (req, res) => {
    const currentUserId = req.user.id;
    const { name, email } = req.body;
    
    // SECURE: Always use authenticated user's ID, never trust client
    const targetUserId = currentUserId;
    
    try {
        // Validate input
        if (!name || !email) {
            return res.status(400).json({ 
                error: 'Name and email are required' 
            });
        }
        
        // Email validation
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(email)) {
            return res.status(400).json({ 
                error: 'Invalid email format' 
            });
        }
        
        // Check if email is already taken by another user
        const existingUser = await User.findOne({ 
            email: email, 
            _id: { $ne: targetUserId } 
        });
        
        if (existingUser) {
            return res.status(400).json({ 
                error: 'Email already in use' 
            });
        }
        
        // Update only allowed fields
        const updatedUser = await User.findByIdAndUpdate(
            targetUserId,
            {
                name: name,
                email: email,
                updatedAt: new Date()
            },
            { new: true, select: 'id username name email updatedAt' }
        );
        
        if (!updatedUser) {
            return res.status(404).json({ error: 'User not found' });
        }
        
        res.json({
            message: 'Profile updated successfully',
            user: updatedUser
        });
        
    } catch (error) {
        console.error('Error updating profile:', error);
        res.status(500).json({ error: 'Update failed' });
    }
});

// SECURE: Delete operations with proper authorization
app.delete('/post/:id', async (req, res) => {
    const { id } = req.params;
    const currentUserId = req.user.id;
    
    try {
        const post = await Post.findById(id);
        
        if (!post) {
            return res.status(404).json({ error: 'Post not found' });
        }
        
        // Check if user can delete this post
        const currentUser = await User.findById(currentUserId);
        const canDelete = post.authorId === currentUserId || 
                         ['admin', 'moderator'].includes(currentUser.role);
        
        if (!canDelete) {
            return res.status(403).json({ 
                error: 'You can only delete your own posts' 
            });
        }
        
        // Soft delete for audit trail
        await Post.findByIdAndUpdate(id, {
            isDeleted: true,
            deletedBy: currentUserId,
            deletedAt: new Date()
        });
        
        // Log deletion for audit
        console.info('Post deleted:', {
            postId: id,
            deletedBy: currentUserId,
            originalAuthor: post.authorId,
            timestamp: new Date().toISOString()
        });
        
        res.json({ message: 'Post deleted successfully' });
        
    } catch (error) {
        console.error('Error deleting post:', error);
        res.status(500).json({ error: 'Deletion failed' });
    }
});
2

Use Indirect Object References and UUID-Based Identifiers

Replace predictable sequential IDs with UUIDs or implement indirect object references that map to internal IDs. This prevents enumeration attacks and makes it much harder for attackers to guess valid object references.

View implementation – JAVASCRIPT
// SECURE: Indirect object references and UUID-based identifiers
const { v4: uuidv4 } = require('uuid');
const crypto = require('crypto');
const express = require('express');

// SECURE: Reference mapping system
class SecureReferenceManager {
    constructor() {
        this.referenceMap = new Map();
        this.reverseMap = new Map();
        this.expirationTimes = new Map();
    }
    
    // Generate secure indirect reference
    generateReference(internalId, userId, resourceType, ttl = 3600000) {
        // Create unique reference tied to user and resource
        const referenceData = {
            internalId: internalId,
            userId: userId,
            resourceType: resourceType,
            createdAt: Date.now(),
            expiresAt: Date.now() + ttl
        };
        
        // Generate cryptographically secure reference
        const reference = crypto.randomBytes(16).toString('hex');
        
        // Store mappings
        this.referenceMap.set(reference, referenceData);
        this.reverseMap.set(`${userId}:${resourceType}:${internalId}`, reference);
        this.expirationTimes.set(reference, referenceData.expiresAt);
        
        return reference;
    }
    
    // Resolve reference to internal ID with validation
    resolveReference(reference, userId, resourceType) {
        const referenceData = this.referenceMap.get(reference);
        
        if (!referenceData) {
            throw new Error('Invalid reference');
        }
        
        // Check expiration
        if (Date.now() > referenceData.expiresAt) {
            this.cleanupReference(reference);
            throw new Error('Reference expired');
        }
        
        // Validate user and resource type
        if (referenceData.userId !== userId || 
            referenceData.resourceType !== resourceType) {
            throw new Error('Access denied');
        }
        
        return referenceData.internalId;
    }
    
    // Clean up expired references
    cleanupReference(reference) {
        const referenceData = this.referenceMap.get(reference);
        if (referenceData) {
            const reverseKey = `${referenceData.userId}:${referenceData.resourceType}:${referenceData.internalId}`;
            this.reverseMap.delete(reverseKey);
        }
        
        this.referenceMap.delete(reference);
        this.expirationTimes.delete(reference);
    }
    
    // Periodic cleanup of expired references
    startCleanupTimer() {
        setInterval(() => {
            const now = Date.now();
            for (const [reference, expirationTime] of this.expirationTimes) {
                if (now > expirationTime) {
                    this.cleanupReference(reference);
                }
            }
        }, 300000); // Clean up every 5 minutes
    }
}

// SECURE: UUID-based model with proper reference management
class SecureUser {
    constructor(userData) {
        this.id = uuidv4(); // Use UUID instead of sequential ID
        this.username = userData.username;
        this.email = userData.email;
        this.role = userData.role || 'user';
        this.createdAt = new Date();
        this.isActive = true;
    }
    
    // Generate secure public reference
    getPublicReference(referenceManager, requestingUserId) {
        return referenceManager.generateReference(
            this.id, 
            requestingUserId, 
            'user',
            1800000 // 30 minutes TTL
        );
    }
    
    // Get sanitized public data
    getPublicData() {
        return {
            username: this.username,
            role: this.role,
            createdAt: this.createdAt,
            isActive: this.isActive
        };
    }
    
    // Get personal data (only for the user themselves)
    getPersonalData() {
        return {
            id: this.id,
            username: this.username,
            email: this.email,
            role: this.role,
            createdAt: this.createdAt,
            isActive: this.isActive
        };
    }
}

// SECURE: Document model with UUID and access control
class SecureDocument {
    constructor(documentData, ownerId) {
        this.id = uuidv4();
        this.title = documentData.title;
        this.content = documentData.content;
        this.ownerId = ownerId;
        this.createdAt = new Date();
        this.isPublic = documentData.isPublic || false;
        this.accessLevel = documentData.accessLevel || 'private';
    }
    
    // Generate time-limited access token
    generateAccessToken(userId, accessType = 'read') {
        const tokenData = {
            documentId: this.id,
            userId: userId,
            accessType: accessType,
            expiresAt: Date.now() + 600000 // 10 minutes
        };
        
        // Create signed token
        const token = crypto.createHmac('sha256', process.env.SECRET_KEY)
            .update(JSON.stringify(tokenData))
            .digest('hex');
        
        return {
            token: token,
            data: Buffer.from(JSON.stringify(tokenData)).toString('base64')
        };
    }
    
    // Validate access token
    static validateAccessToken(tokenStr, dataStr) {
        try {
            const tokenData = JSON.parse(Buffer.from(dataStr, 'base64').toString());
            
            // Check expiration
            if (Date.now() > tokenData.expiresAt) {
                return null;
            }
            
            // Verify token signature
            const expectedToken = crypto.createHmac('sha256', process.env.SECRET_KEY)
                .update(JSON.stringify(tokenData))
                .digest('hex');
            
            if (tokenStr !== expectedToken) {
                return null;
            }
            
            return tokenData;
        } catch (error) {
            return null;
        }
    }
}

// SECURE: Express application with indirect references
const app = express();
const referenceManager = new SecureReferenceManager();
referenceManager.startCleanupTimer();

app.use(express.json());

// SECURE: Get user list with indirect references
app.get('/users', async (req, res) => {
    const currentUserId = req.user.id;
    const { page = 1, limit = 20 } = req.query;
    
    try {
        const pageNum = Math.max(1, parseInt(page));
        const limitNum = Math.min(50, Math.max(1, parseInt(limit)));
        
        // Get users with pagination
        const users = await User.find({ isActive: true })
            .select('username role createdAt')
            .limit(limitNum)
            .skip((pageNum - 1) * limitNum)
            .sort({ createdAt: -1 });
        
        // Generate indirect references for each user
        const usersWithReferences = users.map(user => {
            const publicReference = referenceManager.generateReference(
                user.id,
                currentUserId,
                'user',
                1800000 // 30 minutes
            );
            
            return {
                reference: publicReference,
                username: user.username,
                role: user.role,
                createdAt: user.createdAt
            };
        });
        
        res.json({
            users: usersWithReferences,
            pagination: {
                page: pageNum,
                limit: limitNum,
                hasMore: users.length === limitNum
            }
        });
        
    } catch (error) {
        console.error('Error fetching users:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: Access user by indirect reference
app.get('/user/:reference', async (req, res) => {
    const { reference } = req.params;
    const currentUserId = req.user.id;
    
    try {
        // Resolve reference to internal ID
        const internalUserId = referenceManager.resolveReference(
            reference,
            currentUserId,
            'user'
        );
        
        const user = await User.findById(internalUserId);
        
        if (!user || !user.isActive) {
            return res.status(404).json({ error: 'User not found' });
        }
        
        // Check access permissions
        let userData;
        if (internalUserId === currentUserId) {
            // Own profile - return personal data
            userData = user.getPersonalData();
        } else {
            // Other user - return public data only
            userData = user.getPublicData();
        }
        
        res.json(userData);
        
    } catch (error) {
        if (error.message === 'Invalid reference' || 
            error.message === 'Reference expired' ||
            error.message === 'Access denied') {
            return res.status(403).json({ error: 'Access denied' });
        }
        
        console.error('Error accessing user:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: Document access with token-based authorization
app.get('/document/:docId/access', async (req, res) => {
    const { docId } = req.params;
    const currentUserId = req.user.id;
    
    try {
        // Validate UUID format
        if (!isValidUUID(docId)) {
            return res.status(400).json({ error: 'Invalid document ID format' });
        }
        
        const document = await Document.findById(docId);
        
        if (!document) {
            return res.status(404).json({ error: 'Document not found' });
        }
        
        // Check access permissions
        let hasAccess = false;
        let accessType = 'none';
        
        if (document.ownerId === currentUserId) {
            hasAccess = true;
            accessType = 'owner';
        } else if (document.isPublic) {
            hasAccess = true;
            accessType = 'read';
        } else {
            // Check explicit permissions
            const permission = await DocumentPermission.findOne({
                documentId: docId,
                userId: currentUserId,
                isActive: true
            });
            
            if (permission) {
                hasAccess = true;
                accessType = permission.accessType;
            }
        }
        
        if (!hasAccess) {
            return res.status(403).json({ error: 'Access denied' });
        }
        
        // Generate time-limited access token
        const accessToken = document.generateAccessToken(currentUserId, accessType);
        
        res.json({
            accessToken: accessToken.token,
            accessData: accessToken.data,
            accessType: accessType,
            expiresIn: 600 // 10 minutes
        });
        
    } catch (error) {
        console.error('Error generating document access:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: Document content access using access token
app.get('/document/:docId/content', async (req, res) => {
    const { docId } = req.params;
    const { token, data } = req.query;
    
    try {
        // Validate access token
        const tokenData = SecureDocument.validateAccessToken(token, data);
        
        if (!tokenData || tokenData.documentId !== docId) {
            return res.status(403).json({ error: 'Invalid or expired access token' });
        }
        
        const document = await Document.findById(docId);
        
        if (!document) {
            return res.status(404).json({ error: 'Document not found' });
        }
        
        // Return content based on access type
        const response = {
            id: document.id,
            title: document.title,
            createdAt: document.createdAt,
            accessType: tokenData.accessType
        };
        
        if (['read', 'write', 'owner'].includes(tokenData.accessType)) {
            response.content = document.content;
        }
        
        if (['write', 'owner'].includes(tokenData.accessType)) {
            response.metadata = document.metadata;
        }
        
        // Log access for audit
        await DocumentAccessLog.create({
            documentId: docId,
            userId: tokenData.userId,
            accessType: tokenData.accessType,
            timestamp: new Date(),
            ipAddress: req.ip
        });
        
        res.json(response);
        
    } catch (error) {
        console.error('Error accessing document content:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// SECURE: File sharing with temporary URLs
app.post('/file/:fileId/share', async (req, res) => {
    const { fileId } = req.params;
    const { expiresIn = 3600 } = req.body; // Default 1 hour
    const currentUserId = req.user.id;
    
    try {
        if (!isValidUUID(fileId)) {
            return res.status(400).json({ error: 'Invalid file ID format' });
        }
        
        const file = await File.findById(fileId);
        
        if (!file) {
            return res.status(404).json({ error: 'File not found' });
        }
        
        // Check if user owns the file
        if (file.ownerId !== currentUserId) {
            return res.status(403).json({ error: 'Access denied' });
        }
        
        // Generate temporary share token
        const shareData = {
            fileId: fileId,
            ownerId: currentUserId,
            createdAt: Date.now(),
            expiresAt: Date.now() + (expiresIn * 1000)
        };
        
        const shareToken = crypto.randomBytes(32).toString('hex');
        
        // Store in Redis or database with TTL
        await ShareToken.create({
            token: shareToken,
            data: shareData,
            expiresAt: new Date(shareData.expiresAt)
        });
        
        res.json({
            shareUrl: `/share/${shareToken}`,
            expiresAt: new Date(shareData.expiresAt),
            expiresIn: expiresIn
        });
        
    } catch (error) {
        console.error('Error creating file share:', error);
        res.status(500).json({ error: 'Server error' });
    }
});

// Helper function to validate UUID format
function isValidUUID(uuid) {
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
    return uuidRegex.test(uuid);
}

// SECURE: Search with proper filtering
app.get('/search', async (req, res) => {
    const { query, type = 'all', page = 1, limit = 20 } = req.query;
    const currentUserId = req.user.id;
    
    try {
        if (!query || query.length < 3) {
            return res.status(400).json({ 
                error: 'Query must be at least 3 characters' 
            });
        }
        
        const pageNum = Math.max(1, parseInt(page));
        const limitNum = Math.min(50, Math.max(1, parseInt(limit)));
        
        const results = [];
        
        // Search only accessible resources
        if (type === 'all' || type === 'documents') {
            const documents = await Document.find({
                $and: [
                    {
                        $or: [
                            { ownerId: currentUserId },
                            { isPublic: true },
                            { 
                                id: { 
                                    $in: await getAccessibleDocumentIds(currentUserId) 
                                } 
                            }
                        ]
                    },
                    {
                        $or: [
                            { title: { $regex: query, $options: 'i' } },
                            { content: { $regex: query, $options: 'i' } }
                        ]
                    }
                ]
            })
            .select('title createdAt ownerId isPublic')
            .limit(limitNum)
            .skip((pageNum - 1) * limitNum);
            
            documents.forEach(doc => {
                results.push({
                    type: 'document',
                    reference: referenceManager.generateReference(
                        doc.id, currentUserId, 'document', 1800000
                    ),
                    title: doc.title,
                    createdAt: doc.createdAt,
                    isOwner: doc.ownerId === currentUserId
                });
            });
        }
        
        res.json({
            query: query,
            results: results,
            pagination: {
                page: pageNum,
                limit: limitNum,
                hasMore: results.length === limitNum
            }
        });
        
    } catch (error) {
        console.error('Search error:', error);
        res.status(500).json({ error: 'Search failed' });
    }
});

// Helper function to get accessible document IDs
async function getAccessibleDocumentIds(userId) {
    const permissions = await DocumentPermission.find({
        userId: userId,
        isActive: true
    }).select('documentId');
    
    return permissions.map(p => p.documentId);
}

app.listen(3000, () => {
    console.log('Secure API server running on port 3000');
    console.log('IDOR protections enabled:');
    console.log('- UUID-based identifiers');
    console.log('- Indirect object references');
    console.log('- Token-based access control');
    console.log('- Proper authorization checks');
});
3

Implement Server-Side Authorization and Session Security

Never trust client-side data for authorization decisions. Implement all authorization logic on the server side, use secure session management, and validate all user inputs against server-side permissions before performing any operations.

View implementation – JAVASCRIPT
// SECURE: Server-side authorization and session security
const express = require('express');
const session = require('express-session');
const MongoStore = require('connect-mongo');
const crypto = require('crypto');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');

const app = express();

// SECURE: Security headers and rate limiting
app.use(helmet({
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'"],
            styleSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ["'self'", "data:", "https:"],
            connectSrc: ["'self'"]
        }
    },
    hsts: {
        maxAge: 31536000,
        includeSubDomains: true,
        preload: true
    }
}));

// Rate limiting
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // limit each IP to 100 requests per windowMs
    message: 'Too many requests from this IP'
});
app.use(limiter);

// SECURE: Session configuration
app.use(session({
    secret: process.env.SESSION_SECRET || crypto.randomBytes(64).toString('hex'),
    name: 'sessionId', // Don't use default session name
    resave: false,
    saveUninitialized: false,
    store: MongoStore.create({
        mongoUrl: process.env.MONGODB_URI || 'mongodb://localhost/sessions',
        touchAfter: 24 * 3600 // lazy session update
    }),
    cookie: {
        secure: process.env.NODE_ENV === 'production', // HTTPS only in production
        httpOnly: true, // Prevent XSS
        maxAge: 30 * 60 * 1000, // 30 minutes
        sameSite: 'strict' // CSRF protection
    },
    genid: () => {
        // Generate cryptographically secure session IDs
        return crypto.randomBytes(32).toString('hex');
    }
}));

app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// SECURE: Server-side permission system
class PermissionManager {
    constructor() {
        this.permissions = new Map();
        this.roleHierarchy = {
            superadmin: ['admin', 'manager', 'user', 'guest'],
            admin: ['manager', 'user', 'guest'],
            manager: ['user', 'guest'],
            user: ['guest'],
            guest: []
        };
    }
    
    // Define permissions for resources and actions
    definePermissions() {
        const perms = {
            'user:read': ['superadmin', 'admin', 'manager'],
            'user:write': ['superadmin', 'admin'],
            'user:delete': ['superadmin'],
            'user:read-own': ['user'],
            'user:write-own': ['user'],
            
            'document:read': ['superadmin', 'admin', 'manager', 'user'],
            'document:write': ['superadmin', 'admin', 'manager', 'user'],
            'document:delete': ['superadmin', 'admin', 'manager'],
            'document:admin': ['superadmin', 'admin'],
            
            'order:read': ['superadmin', 'admin', 'manager'],
            'order:write': ['superadmin', 'admin', 'manager'],
            'order:read-own': ['user'],
            
            'file:read': ['superadmin', 'admin', 'manager', 'user'],
            'file:write': ['superadmin', 'admin', 'manager', 'user'],
            'file:delete': ['superadmin', 'admin', 'manager'],
            
            'admin:users': ['superadmin', 'admin'],
            'admin:settings': ['superadmin'],
            'admin:audit': ['superadmin', 'admin']
        };
        
        for (const [permission, roles] of Object.entries(perms)) {
            this.permissions.set(permission, new Set(roles));
        }
    }
    
    // Check if user has permission
    hasPermission(userRole, permission) {
        const allowedRoles = this.permissions.get(permission);
        if (!allowedRoles) {
            return false;
        }
        
        // Check direct permission
        if (allowedRoles.has(userRole)) {
            return true;
        }
        
        // Check inherited permissions through role hierarchy
        const inheritedRoles = this.roleHierarchy[userRole] || [];
        return inheritedRoles.some(role => allowedRoles.has(role));
    }
    
    // Check resource ownership
    async checkOwnership(userId, resourceType, resourceId) {
        try {
            let resource;
            
            switch (resourceType) {
                case 'document':
                    resource = await Document.findById(resourceId).select('ownerId');
                    break;
                case 'file':
                    resource = await File.findById(resourceId).select('ownerId');
                    break;
                case 'order':
                    resource = await Order.findById(resourceId).select('customerId');
                    return resource && resource.customerId === userId;
                default:
                    return false;
            }
            
            return resource && resource.ownerId === userId;
        } catch (error) {
            console.error('Ownership check error:', error);
            return false;
        }
    }
    
    // Comprehensive permission check
    async canPerformAction(user, action, resourceType, resourceId = null) {
        if (!user || !user.role) {
            return false;
        }
        
        const permission = `${resourceType}:${action}`;
        const ownPermission = `${resourceType}:${action}-own`;
        
        // Check general permission
        if (this.hasPermission(user.role, permission)) {
            return true;
        }
        
        // Check ownership-based permission
        if (resourceId && this.hasPermission(user.role, ownPermission)) {
            return await this.checkOwnership(user.id, resourceType, resourceId);
        }
        
        return false;
    }
}

const permissionManager = new PermissionManager();
permissionManager.definePermissions();

// SECURE: Authentication middleware
function requireAuth(req, res, next) {
    if (!req.session || !req.session.user) {
        return res.status(401).json({ 
            error: 'Authentication required',
            redirectTo: '/login'
        });
    }
    
    // Validate session integrity
    if (!req.session.user.id || !req.session.user.role) {
        req.session.destroy();
        return res.status(401).json({ 
            error: 'Invalid session',
            redirectTo: '/login'
        });
    }
    
    req.user = req.session.user;
    next();
}

// SECURE: Permission middleware factory
function requirePermission(permission) {
    return async (req, res, next) => {
        if (!req.user) {
            return res.status(401).json({ error: 'Authentication required' });
        }
        
        const hasPermission = permissionManager.hasPermission(req.user.role, permission);
        
        if (!hasPermission) {
            // Log unauthorized access attempt
            console.warn('Unauthorized access attempt:', {
                userId: req.user.id,
                userRole: req.user.role,
                requiredPermission: permission,
                endpoint: req.originalUrl,
                method: req.method,
                ip: req.ip,
                userAgent: req.get('User-Agent'),
                timestamp: new Date().toISOString()
            });
            
            return res.status(403).json({ 
                error: 'Insufficient permissions',
                required: permission
            });
        }
        
        next();
    };
}

// SECURE: Resource-specific authorization middleware
function requireResourceAccess(resourceType, action) {
    return async (req, res, next) => {
        if (!req.user) {
            return res.status(401).json({ error: 'Authentication required' });
        }
        
        const resourceId = req.params.id || req.params.docId || req.params.fileId;
        
        try {
            const canAccess = await permissionManager.canPerformAction(
                req.user,
                action,
                resourceType,
                resourceId
            );
            
            if (!canAccess) {
                console.warn('Resource access denied:', {
                    userId: req.user.id,
                    resourceType,
                    resourceId,
                    action,
                    timestamp: new Date().toISOString()
                });
                
                return res.status(403).json({ 
                    error: 'Access denied to this resource' 
                });
            }
            
            next();
            
        } catch (error) {
            console.error('Authorization check error:', error);
            res.status(500).json({ error: 'Authorization check failed' });
        }
    };
}

// SECURE: Login endpoint
app.post('/login', async (req, res) => {
    const { username, password } = req.body;
    
    try {
        // Input validation
        if (!username || !password) {
            return res.status(400).json({ 
                error: 'Username and password required' 
            });
        }
        
        // Authenticate user (implement proper password verification)
        const user = await User.findOne({ username }).select('+passwordHash');
        
        if (!user || !await bcrypt.compare(password, user.passwordHash)) {
            // Log failed login attempt
            console.warn('Failed login attempt:', {
                username,
                ip: req.ip,
                userAgent: req.get('User-Agent'),
                timestamp: new Date().toISOString()
            });
            
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        // Create secure session
        req.session.user = {
            id: user.id,
            username: user.username,
            role: user.role,
            loginTime: new Date().toISOString()
        };
        
        // Regenerate session ID to prevent session fixation
        req.session.regenerate((err) => {
            if (err) {
                console.error('Session regeneration error:', err);
                return res.status(500).json({ error: 'Login failed' });
            }
            
            req.session.user = {
                id: user.id,
                username: user.username,
                role: user.role,
                loginTime: new Date().toISOString()
            };
            
            // Log successful login
            console.info('Successful login:', {
                userId: user.id,
                username: user.username,
                ip: req.ip,
                timestamp: new Date().toISOString()
            });
            
            res.json({
                message: 'Login successful',
                user: {
                    id: user.id,
                    username: user.username,
                    role: user.role
                }
            });
        });
        
    } catch (error) {
        console.error('Login error:', error);
        res.status(500).json({ error: 'Login failed' });
    }
});

// SECURE: Profile endpoint with server-side authorization
app.get('/profile', requireAuth, async (req, res) => {
    try {
        // SECURE: Always use session user ID, never trust client
        const userId = req.user.id;
        
        const user = await User.findById(userId)
            .select('username email role createdAt lastLogin preferences');
        
        if (!user) {
            req.session.destroy();
            return res.status(401).json({ error: 'User not found' });
        }
        
        res.json({
            user: {
                id: user.id,
                username: user.username,
                email: user.email,
                role: user.role,
                createdAt: user.createdAt,
                lastLogin: user.lastLogin,
                preferences: user.preferences
            },
            session: {
                loginTime: req.user.loginTime,
                expiresAt: new Date(Date.now() + req.session.cookie.maxAge)
            }
        });
        
    } catch (error) {
        console.error('Profile fetch error:', error);
        res.status(500).json({ error: 'Failed to fetch profile' });
    }
});

// SECURE: Update profile with proper validation
app.put('/profile', requireAuth, async (req, res) => {
    try {
        // SECURE: Use session user ID only
        const userId = req.user.id;
        const { name, email, preferences } = req.body;
        
        // Server-side validation
        const updates = {};
        
        if (name) {
            if (typeof name !== 'string' || name.length < 1 || name.length > 100) {
                return res.status(400).json({ 
                    error: 'Name must be 1-100 characters' 
                });
            }
            updates.name = name.trim();
        }
        
        if (email) {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            if (!emailRegex.test(email)) {
                return res.status(400).json({ 
                    error: 'Invalid email format' 
                });
            }
            
            // Check email uniqueness
            const existingUser = await User.findOne({ 
                email: email, 
                _id: { $ne: userId } 
            });
            
            if (existingUser) {
                return res.status(400).json({ 
                    error: 'Email already in use' 
                });
            }
            
            updates.email = email.toLowerCase().trim();
        }
        
        if (preferences) {
            // Validate preferences object
            if (typeof preferences !== 'object') {
                return res.status(400).json({ 
                    error: 'Invalid preferences format' 
                });
            }
            
            // Sanitize preferences (whitelist allowed fields)
            const allowedPrefs = ['theme', 'language', 'notifications', 'timezone'];
            const sanitizedPrefs = {};
            
            for (const [key, value] of Object.entries(preferences)) {
                if (allowedPrefs.includes(key)) {
                    sanitizedPrefs[key] = value;
                }
            }
            
            updates.preferences = sanitizedPrefs;
        }
        
        // Update user
        const updatedUser = await User.findByIdAndUpdate(
            userId,
            { ...updates, updatedAt: new Date() },
            { new: true, select: 'username email name preferences updatedAt' }
        );
        
        res.json({
            message: 'Profile updated successfully',
            user: updatedUser
        });
        
    } catch (error) {
        console.error('Profile update error:', error);
        res.status(500).json({ error: 'Profile update failed' });
    }
});

// SECURE: Document access with comprehensive authorization
app.get('/document/:id', 
    requireAuth, 
    requireResourceAccess('document', 'read'), 
    async (req, res) => {
        const { id } = req.params;
        const userId = req.user.id;
        
        try {
            const document = await Document.findById(id)
                .populate('ownerId', 'username')
                .select('title content ownerId createdAt updatedAt isPublic accessLevel');
            
            if (!document) {
                return res.status(404).json({ error: 'Document not found' });
            }
            
            // Determine access level for response
            const isOwner = document.ownerId._id.toString() === userId;
            const hasAdminPermission = permissionManager.hasPermission(req.user.role, 'document:admin');
            
            const response = {
                id: document.id,
                title: document.title,
                content: document.content,
                owner: document.ownerId.username,
                createdAt: document.createdAt,
                isOwner: isOwner,
                canEdit: isOwner || hasAdminPermission,
                canDelete: isOwner || hasAdminPermission
            };
            
            // Log document access
            console.info('Document accessed:', {
                documentId: id,
                userId: userId,
                isOwner: isOwner,
                timestamp: new Date().toISOString()
            });
            
            res.json(response);
            
        } catch (error) {
            console.error('Document access error:', error);
            res.status(500).json({ error: 'Failed to fetch document' });
        }
    }
);

// SECURE: Admin user management
app.get('/admin/users', 
    requireAuth, 
    requirePermission('admin:users'), 
    async (req, res) => {
        const { page = 1, limit = 50, search } = req.query;
        
        try {
            const pageNum = Math.max(1, parseInt(page));
            const limitNum = Math.min(100, Math.max(1, parseInt(limit)));
            
            // Build query
            let query = {};
            if (search) {
                query = {
                    $or: [
                        { username: { $regex: search, $options: 'i' } },
                        { email: { $regex: search, $options: 'i' } }
                    ]
                };
            }
            
            const users = await User.find(query)
                .select('username email role isActive createdAt lastLogin')
                .skip((pageNum - 1) * limitNum)
                .limit(limitNum)
                .sort({ createdAt: -1 });
            
            const totalUsers = await User.countDocuments(query);
            
            // Log admin access
            console.info('Admin users list accessed:', {
                adminId: req.user.id,
                search: search || 'none',
                page: pageNum,
                timestamp: new Date().toISOString()
            });
            
            res.json({
                users: users,
                pagination: {
                    page: pageNum,
                    limit: limitNum,
                    total: totalUsers,
                    pages: Math.ceil(totalUsers / limitNum)
                }
            });
            
        } catch (error) {
            console.error('Admin users fetch error:', error);
            res.status(500).json({ error: 'Failed to fetch users' });
        }
    }
);

// SECURE: Logout with session cleanup
app.post('/logout', requireAuth, (req, res) => {
    const userId = req.user.id;
    
    req.session.destroy((err) => {
        if (err) {
            console.error('Session destruction error:', err);
            return res.status(500).json({ error: 'Logout failed' });
        }
        
        res.clearCookie('sessionId');
        
        console.info('User logged out:', {
            userId: userId,
            timestamp: new Date().toISOString()
        });
        
        res.json({ message: 'Logged out successfully' });
    });
});

// SECURE: Session validation endpoint
app.get('/session/validate', (req, res) => {
    if (req.session && req.session.user) {
        res.json({
            valid: true,
            user: {
                id: req.session.user.id,
                username: req.session.user.username,
                role: req.session.user.role
            },
            expiresAt: new Date(Date.now() + req.session.cookie.maxAge)
        });
    } else {
        res.json({ valid: false });
    }
});

// Global error handler
app.use((error, req, res, next) => {
    console.error('Unhandled error:', {
        error: error.message,
        stack: error.stack,
        url: req.originalUrl,
        method: req.method,
        user: req.user?.id,
        timestamp: new Date().toISOString()
    });
    
    res.status(500).json({ 
        error: 'Internal server error' 
    });
});

app.listen(3000, () => {
    console.log('Secure server running on port 3000');
    console.log('Security features enabled:');
    console.log('- Server-side authorization');
    console.log('- Secure session management');
    console.log('- Permission-based access control');
    console.log('- Input validation and sanitization');
    console.log('- Comprehensive audit logging');
});

Detect This Vulnerability in Your Code

Sourcery automatically identifies insecure direct object references (idor) and many other security issues in your codebase.