defense-in-depth
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDefense-in-Depth Validation
纵深防御验证
Overview
概述
When you fix a bug caused by invalid data, adding validation at one place feels sufficient. But that single check can be bypassed by different code paths, refactoring, or mocks.
Core principle: Validate at EVERY layer data passes through. Make the bug structurally impossible.
当你修复一个由无效数据引发的Bug时,在单一位置添加验证看似足够。但这种单一检查可能会被不同的代码路径、重构或模拟(mock)绕过。
核心原则: 在数据流经的每一层都进行验证,从结构上让这类Bug无法出现。
When to Use
适用场景
Use when:
- Invalid data caused a bug deep in the call stack
- Data crosses system boundaries (API → service → storage)
- Multiple code paths can reach the same vulnerable code
- Tests mock intermediate layers (bypassing validation)
Don't use when:
- Pure internal function with single caller (validate at caller)
- Data already validated by framework/library you trust
- Adding validation would duplicate identical checks at adjacent layers
适用情况:
- 无效数据导致调用栈深层出现Bug
- 数据跨系统边界流转(API → 服务 → 存储)
- 多条代码路径可到达同一易受攻击的代码
- 测试中模拟了中间层(绕过了验证)
不适用情况:
- 仅单一调用方的纯内部函数(在调用方处验证即可)
- 数据已由你信任的框架/库完成验证
- 在相邻层级添加完全相同的验证检查
The Four Layers
四个验证层级
Layer 1: Entry Point Validation
层级1:入口点验证
Purpose: Reject invalid input at API/system boundary
typescript
function createProject(name: string, workingDirectory: string) {
if (!workingDirectory?.trim()) {
throw new Error('workingDirectory cannot be empty');
}
if (!existsSync(workingDirectory)) {
throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
}
// ... proceed
}When needed: Always. This is your first line of defense.
目的: 在API/系统边界处拒绝无效输入
typescript
function createProject(name: string, workingDirectory: string) {
if (!workingDirectory?.trim()) {
throw new Error('workingDirectory cannot be empty');
}
if (!existsSync(workingDirectory)) {
throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
}
// ... proceed
}适用时机: 始终需要,这是你的第一道防线。
Layer 2: Business Logic Validation
层级2:业务逻辑验证
Purpose: Ensure data makes sense for this specific operation
typescript
function initializeWorkspace(projectDir: string, sessionId: string) {
if (!projectDir) {
throw new Error('projectDir required for workspace initialization');
}
// ... proceed
}When needed: When business rules differ from entry validation, or when mocks might bypass Layer 1.
目的: 确保数据对当前特定操作有意义
typescript
function initializeWorkspace(projectDir: string, sessionId: string) {
if (!projectDir) {
throw new Error('projectDir required for workspace initialization');
}
// ... proceed
}适用时机: 当业务规则与入口验证不同,或模拟可能绕过层级1时。
Layer 3: Environment Guards
层级3:环境防护
Purpose: Prevent dangerous operations in specific contexts
typescript
async function gitInit(directory: string) {
if (process.env.NODE_ENV === 'test') {
const normalized = normalize(resolve(directory));
if (!normalized.startsWith(tmpdir())) {
throw new Error(`Refusing git init outside temp dir in tests: ${directory}`);
}
}
// ... proceed
}When needed: When operation is destructive/irreversible, especially in test environments.
目的: 防止在特定上下文执行危险操作
typescript
async function gitInit(directory: string) {
if (process.env.NODE_ENV === 'test') {
const normalized = normalize(resolve(directory));
if (!normalized.startsWith(tmpdir())) {
throw new Error(`Refusing git init outside temp dir in tests: ${directory}`);
}
}
// ... proceed
}适用时机: 当操作具有破坏性/不可逆性时,尤其是在测试环境中。
Layer 4: Debug Instrumentation
层级4:调试埋点
Purpose: Capture context for forensics when other layers fail
typescript
async function gitInit(directory: string) {
logger.debug('git init', { directory, cwd: process.cwd(), stack: new Error().stack });
// ... proceed
}When needed: When debugging is difficult, or when you need to trace how bad data arrived.
目的: 当其他层级验证失败时,捕获用于取证的上下文信息
typescript
async function gitInit(directory: string) {
logger.debug('git init', { directory, cwd: process.cwd(), stack: new Error().stack });
// ... proceed
}适用时机: 当调试难度大,或需要追踪无效数据的来源时。
Decision Heuristic
决策启发式
| Situation | Layers Needed |
|---|---|
| Public API, simple validation | 1 only |
| Data crosses multiple services | 1 + 2 |
| Destructive operations (delete, init, write) | 1 + 2 + 3 |
| Chasing a hard-to-reproduce bug | 1 + 2 + 3 + 4 |
| Tests mock intermediate layers | At minimum: 1 + 3 |
| 场景 | 需要启用的层级 |
|---|---|
| 公开API,简单验证 | 仅层级1 |
| 数据跨多个服务流转 | 层级1 + 层级2 |
| 破坏性操作(删除、初始化、写入) | 层级1 + 层级2 + 层级3 |
| 排查难以复现的Bug | 层级1 + 层级2 + 层级3 + 层级4 |
| 测试中模拟中间层 | 至少:层级1 + 层级3 |
Applying the Pattern
模式应用步骤
When you find a bug caused by invalid data:
- Trace the data flow - Where does the bad value originate? Where is it used?
- Map checkpoints - List every function/layer the data passes through
- Decide which layers - Use heuristic above
- Add validation - Entry → business → environment → debug
- Test each layer - Verify Layer 2 catches what bypasses Layer 1
当你发现由无效数据引发的Bug时:
- 追踪数据流 - 无效值的来源在哪里?在哪里被使用?
- 标记检查点 - 列出数据流经的所有函数/层级
- 确定验证层级 - 参考上述启发式规则
- 添加验证 - 按入口→业务→环境→调试的顺序添加
- 测试每个层级 - 验证层级2能捕获绕过层级1的情况
Quick Reference
快速参考
| Layer | Question It Answers | Typical Check |
|---|---|---|
| Entry | Is input valid? | Non-empty, exists, correct type |
| Business | Does it make sense here? | Required for this operation, within bounds |
| Environment | Is this safe in this context? | Not in prod, inside temp dir, etc. |
| Debug | How did we get here? | Log stack, cwd, inputs |
| 层级 | 解决的问题 | 典型检查内容 |
|---|---|---|
| 入口 | 输入是否有效? | 非空、存在、类型正确 |
| 业务 | 在此场景下是否合理? | 当前操作必需、在有效范围内 |
| 环境 | 在此上下文执行是否安全? | 非生产环境、在临时目录内等 |
| 调试 | 我们是如何走到这一步的? | 记录调用栈、当前工作目录、输入参数 |
Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
| One validation point, call it done | Add at least entry + business layers |
| Identical checks at adjacent layers | Make each layer check something different |
| Environment guards only in prod | Add them in test too (prevent test pollution) |
| Skipping debug logging | Add it during the bug hunt, keep it |
| Validation but no useful error message | Include the bad value and expected format |
| 错误做法 | 修复方案 |
|---|---|
| 仅添加一处验证就认为完成 | 至少添加入口+业务两层验证 |
| 在相邻层级添加完全相同的检查 | 让每个层级检查不同的内容 |
| 仅在生产环境添加环境防护 | 在测试环境也添加(防止测试污染) |
| 跳过调试日志 | 在排查Bug时添加,并保留下来 |
| 添加了验证但错误信息无用 | 包含无效值和预期格式 |
Key Insight
核心洞见
During testing, each layer catches bugs the others miss:
- Different code paths bypass entry validation
- Mocks bypass business logic checks
- Edge cases need environment guards
- Debug logging identifies structural misuse
Don't stop at one validation point. The bug isn't fixed until it's impossible.
在测试过程中,每个层级都会捕获其他层级遗漏的Bug:
- 不同代码路径会绕过入口验证
- 模拟会绕过业务逻辑检查
- 边缘情况需要环境防护
- 调试日志可识别结构性误用
不要仅停留在单一验证点。 只有当Bug从结构上无法出现时,才算真正修复。