Error Handling and Information Disclosure: Preventing Security Leaks
Whitespots Team ·
error-handling
information-disclosure
web
Introduction
Poor error handling can leak sensitive information about your application’s internals, helping attackers craft targeted exploits. This guide covers secure error handling practices that protect against information disclosure.
Common Information Disclosure Issues
- Stack traces exposed to users
- Database error messages
- File paths and system information
- Framework and version details
- Internal API structure
- Debug information in production
Vulnerable Error Handling
javascript// VULNERABLE: Exposes internal details app.post('/api/users', async (req, res) => { try { const user = await db.query('INSERT INTO users...'); res.json(user); } catch (error) { // Leaks database structure and SQL! res.status(500).json({ error: error.message, stack: error.stack }); } });
Secure Error Handling
javascript// SECURE: Generic errors to users, detailed logs internally const logger = require('./logger'); class AppError extends Error { constructor(message, statusCode, isOperational = true) { super(message); this.statusCode = statusCode; this.isOperational = isOperational; } } app.post('/api/users', async (req, res) => { try { const user = await createUser(req.body); res.json(user); } catch (error) { logger.error('User creation failed', { error: error.message, stack: error.stack, body: req.body, ip: req.ip }); // Generic message to client res.status(500).json({ error: 'Unable to create user. Please try again later.' }); } }); // Global error handler app.use((err, req, res, next) => { logger.error('Application error', { message: err.message, stack: err.stack, url: req.url, method: req.method }); const statusCode = err.statusCode || 500; const message = err.isOperational ? err.message : 'An unexpected error occurred'; res.status(statusCode).json({ error: message, ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) }); });
Input Validation Errors
javascript// Safe validation error messages const { body, validationResult } = require('express-validator'); app.post('/api/users', body('email').isEmail().withMessage('Invalid email format'), body('password').isLength({ min: 8 }).withMessage('Password too short'), (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { // Safe: only validation messages, no internal details return res.status(400).json({ errors: errors.array().map(e => ({ field: e.param, message: e.msg })) }); } // Continue processing... } );
Production Configuration
javascript// Environment-specific error handling if (process.env.NODE_ENV === 'production') { // Disable stack traces app.set('env', 'production'); // Remove X-Powered-By header app.disable('x-powered-by'); // Generic error pages app.use((err, req, res, next) => { res.status(err.status || 500); res.json({ error: 'Internal server error' }); }); }
Secure Error Handling Checklist
- ✅ Never expose stack traces to users
- ✅ Log detailed errors internally
- ✅ Return generic error messages
- ✅ Sanitize validation error messages
- ✅ Remove server identification headers
- ✅ Different handling for dev vs production
- ✅ Avoid exposing file paths
- ✅ Don’t leak database schema details
- ✅ Monitor error patterns
- ✅ Use custom error pages
Conclusion
Secure error handling balances user experience with security by providing helpful feedback without exposing system internals. Implement comprehensive internal logging while presenting generic, safe messages to end users.