error-sanitization
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseError Sanitization
错误信息脱敏
Production-safe error handling: log everything server-side, expose nothing sensitive to users.
面向生产环境的安全错误处理:在服务器端记录所有错误详情,不向用户暴露任何敏感信息。
When to Use This Skill
适用场景
- Building APIs that return error messages to clients
- Handling exceptions in production environments
- Processing batch operations with partial failures
- Any system where error messages could leak sensitive information
- 开发向客户端返回错误信息的API
- 在生产环境中处理异常
- 处理存在部分失败情况的批量操作
- 任何可能通过错误信息泄露敏感数据的系统
Core Concepts
核心概念
Error messages can leak sensitive information including database connection strings, internal file paths, stack traces, API keys, and business logic details. The solution is to always log full error details server-side for debugging while returning only generic, safe messages to users.
The flow is:
- Exception occurs
- Log FULL error server-side with context
- Classify error type
- Return GENERIC message to user
- Only expose safe, actionable errors (like validation)
错误信息可能泄露敏感数据,包括数据库连接字符串、内部文件路径、堆栈跟踪信息、API密钥及业务逻辑细节。解决方案是:始终在服务器端记录完整的错误详情以供调试,同时仅向用户返回通用、安全的提示信息。
流程如下:
- 发生异常
- 在服务器端记录包含上下文的完整错误信息
- 对错误类型进行分类
- 向用户返回通用提示信息
- 仅暴露安全、可操作的错误(如验证错误)
Implementation
实现方案
Python
Python
python
import os
import logging
from typing import Optional
from fastapi import HTTPException
from pydantic import ValidationError
logger = logging.getLogger(__name__)
class ErrorSanitizer:
"""
Sanitizes error messages to prevent information leakage.
"""
SENSITIVE_PATTERNS = [
"password", "secret", "key", "token", "credential",
"postgresql://", "mysql://", "mongodb://", "redis://",
"localhost", "127.0.0.1", "internal", "0.0.0.0",
"traceback", "exception", "error at", "line ",
"/home/", "/var/", "/etc/", "C:\\",
"SUPABASE", "AWS", "STRIPE", "SENDGRID",
]
@staticmethod
def is_production() -> bool:
return os.getenv("ENVIRONMENT", "development").lower() == "production"
@staticmethod
def sanitize_error(
e: Exception,
user_message: str = "Operation failed",
log_context: Optional[dict] = None
) -> str:
"""Sanitize error message for user display."""
# ALWAYS log full error server-side
logger.error(
f"Error occurred: {type(e).__name__}: {str(e)}",
exc_info=True,
extra=log_context or {}
)
# In development, show more details
if not ErrorSanitizer.is_production():
return f"{user_message}: {str(e)}"
# Validation errors are safe (user input issues)
if isinstance(e, ValidationError):
return f"Validation error: {str(e)}"
# HTTPException with client error (4xx) is safe
if isinstance(e, HTTPException):
if 400 <= e.status_code < 500:
return e.detail
return user_message
# Check for sensitive patterns
error_str = str(e).lower()
for pattern in ErrorSanitizer.SENSITIVE_PATTERNS:
if pattern in error_str:
return user_message
# Short, simple errors without sensitive patterns might be safe
if len(str(e)) < 100 and not any(c in str(e) for c in ['/', '\\', '@', ':']):
return str(e)
return user_message
@staticmethod
def create_http_exception(
e: Exception,
status_code: int = 500,
user_message: str = "Operation failed",
log_context: Optional[dict] = None
) -> HTTPException:
"""Create HTTPException with sanitized error message."""
safe_message = ErrorSanitizer.sanitize_error(e, user_message, log_context)
return HTTPException(status_code=status_code, detail=safe_message)python
import os
import logging
from typing import Optional
from fastapi import HTTPException
from pydantic import ValidationError
logger = logging.getLogger(__name__)
class ErrorSanitizer:
"""
Sanitizes error messages to prevent information leakage.
"""
SENSITIVE_PATTERNS = [
"password", "secret", "key", "token", "credential",
"postgresql://", "mysql://", "mongodb://", "redis://",
"localhost", "127.0.0.1", "internal", "0.0.0.0",
"traceback", "exception", "error at", "line ",
"/home/", "/var/", "/etc/", "C:\\",
"SUPABASE", "AWS", "STRIPE", "SENDGRID",
]
@staticmethod
def is_production() -> bool:
return os.getenv("ENVIRONMENT", "development").lower() == "production"
@staticmethod
def sanitize_error(
e: Exception,
user_message: str = "Operation failed",
log_context: Optional[dict] = None
) -> str:
"""Sanitize error message for user display."""
# ALWAYS log full error server-side
logger.error(
f"Error occurred: {type(e).__name__}: {str(e)}",
exc_info=True,
extra=log_context or {}
)
# In development, show more details
if not ErrorSanitizer.is_production():
return f"{user_message}: {str(e)}"
# Validation errors are safe (user input issues)
if isinstance(e, ValidationError):
return f"Validation error: {str(e)}"
# HTTPException with client error (4xx) is safe
if isinstance(e, HTTPException):
if 400 <= e.status_code < 500:
return e.detail
return user_message
# Check for sensitive patterns
error_str = str(e).lower()
for pattern in ErrorSanitizer.SENSITIVE_PATTERNS:
if pattern in error_str:
return user_message
# Short, simple errors without sensitive patterns might be safe
if len(str(e)) < 100 and not any(c in str(e) for c in ['/', '\\', '@', ':']):
return str(e)
return user_message
@staticmethod
def create_http_exception(
e: Exception,
status_code: int = 500,
user_message: str = "Operation failed",
log_context: Optional[dict] = None
) -> HTTPException:
"""Create HTTPException with sanitized error message."""
safe_message = ErrorSanitizer.sanitize_error(e, user_message, log_context)
return HTTPException(status_code=status_code, detail=safe_message)TypeScript
TypeScript
typescript
import { Logger } from './logger';
const SENSITIVE_PATTERNS = [
'password', 'secret', 'key', 'token', 'credential',
'postgresql://', 'mysql://', 'mongodb://', 'redis://',
'localhost', '127.0.0.1', 'internal', '0.0.0.0',
'/home/', '/var/', '/etc/', 'C:\\',
];
interface SanitizeOptions {
userMessage?: string;
logContext?: Record<string, unknown>;
}
export class ErrorSanitizer {
private static isProduction(): boolean {
return process.env.NODE_ENV === 'production';
}
static sanitize(
error: Error,
options: SanitizeOptions = {}
): string {
const { userMessage = 'Operation failed', logContext = {} } = options;
// Always log full error server-side
Logger.error('Error occurred', {
name: error.name,
message: error.message,
stack: error.stack,
...logContext,
});
// In development, show more details
if (!this.isProduction()) {
return `${userMessage}: ${error.message}`;
}
// Check for sensitive patterns
const errorStr = error.message.toLowerCase();
for (const pattern of SENSITIVE_PATTERNS) {
if (errorStr.includes(pattern)) {
return userMessage;
}
}
// Short, simple errors might be safe
if (error.message.length < 100 && !/[\/\\@:]/.test(error.message)) {
return error.message;
}
return userMessage;
}
static createHttpError(
error: Error,
statusCode: number = 500,
options: SanitizeOptions = {}
): { statusCode: number; message: string } {
return {
statusCode,
message: this.sanitize(error, options),
};
}
}typescript
import { Logger } from './logger';
const SENSITIVE_PATTERNS = [
'password', 'secret', 'key', 'token', 'credential',
'postgresql://', 'mysql://', 'mongodb://', 'redis://',
'localhost', '127.0.0.1', 'internal', '0.0.0.0',
'/home/', '/var/', '/etc/', 'C:\\',
];
interface SanitizeOptions {
userMessage?: string;
logContext?: Record<string, unknown>;
}
export class ErrorSanitizer {
private static isProduction(): boolean {
return process.env.NODE_ENV === 'production';
}
static sanitize(
error: Error,
options: SanitizeOptions = {}
): string {
const { userMessage = 'Operation failed', logContext = {} } = options;
// Always log full error server-side
Logger.error('Error occurred', {
name: error.name,
message: error.message,
stack: error.stack,
...logContext,
});
// In development, show more details
if (!this.isProduction()) {
return `${userMessage}: ${error.message}`;
}
// Check for sensitive patterns
const errorStr = error.message.toLowerCase();
for (const pattern of SENSITIVE_PATTERNS) {
if (errorStr.includes(pattern)) {
return userMessage;
}
}
// Short, simple errors might be safe
if (error.message.length < 100 && !/[\/\\@:]/.test(error.message)) {
return error.message;
}
return userMessage;
}
static createHttpError(
error: Error,
statusCode: number = 500,
options: SanitizeOptions = {}
): { statusCode: number; message: string } {
return {
statusCode,
message: this.sanitize(error, options),
};
}
}Usage Examples
使用示例
Route Handler
路由处理器
python
@router.post("/process")
async def process_invoice(invoice_id: str):
try:
result = await processor.process(invoice_id)
return result
except ValidationError as e:
# Validation errors are safe to expose
raise HTTPException(status_code=400, detail=str(e))
except HTTPException:
raise # Re-raise as-is
except Exception as e:
# All other errors get sanitized
raise ErrorSanitizer.create_http_exception(
e,
status_code=500,
user_message="Failed to process invoice",
log_context={"invoice_id": invoice_id}
)python
@router.post("/process")
async def process_invoice(invoice_id: str):
try:
result = await processor.process(invoice_id)
return result
except ValidationError as e:
# Validation errors are safe to expose
raise HTTPException(status_code=400, detail=str(e))
except HTTPException:
raise # Re-raise as-is
except Exception as e:
# All other errors get sanitized
raise ErrorSanitizer.create_http_exception(
e,
status_code=500,
user_message="Failed to process invoice",
log_context={"invoice_id": invoice_id}
)Domain-Specific Sanitizers
领域特定脱敏器
python
def sanitize_database_error(e: Exception) -> str:
return ErrorSanitizer.sanitize_error(
e,
user_message="Database operation failed. Please try again.",
log_context={"error_type": "database"}
)
def sanitize_api_error(e: Exception, service_name: str = "external service") -> str:
return ErrorSanitizer.sanitize_error(
e,
user_message=f"Failed to communicate with {service_name}.",
log_context={"error_type": "external_api", "service": service_name}
)python
def sanitize_database_error(e: Exception) -> str:
return ErrorSanitizer.sanitize_error(
e,
user_message="Database operation failed. Please try again.",
log_context={"error_type": "database"}
)
def sanitize_api_error(e: Exception, service_name: str = "external service") -> str:
return ErrorSanitizer.sanitize_error(
e,
user_message=f"Failed to communicate with {service_name}.",
log_context={"error_type": "external_api", "service": service_name}
)Batch Operations with Partial Failures
存在部分失败的批量操作
python
def process_items(items: list[dict]) -> dict:
failed_items = []
for idx, item in enumerate(items):
try:
process_item(item)
except Exception as e:
error_type = classify_error(e)
failed_items.append({
"line": idx,
"description": item['description'][:50], # Truncate
"error_type": error_type,
"message": get_user_friendly_message(error_type)
# NOTE: Don't include str(e) - might be sensitive
})
return {
"status": "partial_success" if failed_items else "success",
"failed_items": failed_items
}python
def process_items(items: list[dict]) -> dict:
failed_items = []
for idx, item in enumerate(items):
try:
process_item(item)
except Exception as e:
error_type = classify_error(e)
failed_items.append({
"line": idx,
"description": item['description'][:50], # Truncate
"error_type": error_type,
"message": get_user_friendly_message(error_type)
# NOTE: Don't include str(e) - might be sensitive
})
return {
"status": "partial_success" if failed_items else "success",
"failed_items": failed_items
}Best Practices
最佳实践
- Always log full error details server-side with
exc_info=True - Include correlation IDs (request_id, session_id) in logs for tracing
- Truncate user input in error messages to prevent log injection
- Test error handling in production mode - errors look different in dev vs prod
- Monitor "unknown" error rates - high rates indicate missing classification
- 始终在服务器端使用记录完整错误详情
exc_info=True - 在日志中包含关联ID(request_id、session_id)以便追踪
- 截断错误信息中的用户输入,防止日志注入
- 在生产模式下测试错误处理——开发环境与生产环境的错误表现不同
- 监控“未知”错误的发生率——高发生率表明存在未分类的错误类型
Common Mistakes
常见误区
- Exposing raw exception messages to users in production
- Forgetting to log the full error before sanitizing
- Including sensitive data in log messages (passwords, connection strings)
- Not testing error responses in production mode
- Trusting that "safe" errors don't contain injected content
- 在生产环境中向用户暴露原始异常信息
- 在脱敏前忘记记录完整错误信息
- 在日志中包含敏感数据(密码、连接字符串)
- 未在生产模式下测试错误响应
- 轻信“安全”错误不包含注入内容
Related Patterns
相关模式
- exception-taxonomy - Hierarchical exception system with error codes
- circuit-breaker - Prevent cascading failures
- error-handling - General error handling patterns
- exception-taxonomy - 带错误码的分层异常系统
- circuit-breaker - 防止级联故障
- error-handling - 通用错误处理模式