sharp-edges
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSharp Edges Analysis
尖锐边缘分析
Evaluates whether APIs, configurations, and interfaces are resistant to developer misuse. Identifies designs where the "easy path" leads to insecurity.
评估API、配置和接口是否能抵御开发者误用。识别那些“便捷路径”会导致不安全的设计。
When to Use
适用场景
- Reviewing API or library design decisions
- Auditing configuration schemas for dangerous options
- Evaluating cryptographic API ergonomics
- Assessing authentication/authorization interfaces
- Reviewing any code that exposes security-relevant choices to developers
- 审查API或库的设计决策
- 审计配置架构中的危险选项
- 评估加密API的易用性
- 评估认证/授权接口
- 审查任何向开发者暴露安全相关选项的代码
When NOT to Use
不适用场景
- Implementation bugs (use standard code review)
- Business logic flaws (use domain-specific analysis)
- Performance optimization (different concern)
- 实现漏洞(使用标准代码审查)
- 业务逻辑缺陷(使用领域特定分析)
- 性能优化(属于不同关注点)
Core Principle
核心原则
The pit of success: Secure usage should be the path of least resistance. If developers must understand cryptography, read documentation carefully, or remember special rules to avoid vulnerabilities, the API has failed.
成功之坑:安全用法应是阻力最小的路径。如果开发者必须理解加密技术、仔细阅读文档或记住特殊规则才能避免漏洞,那么这个API就是失败的。
Rationalizations to Reject
需拒绝的合理化借口
| Rationalization | Why It's Wrong | Required Action |
|---|---|---|
| "It's documented" | Developers don't read docs under deadline pressure | Make the secure choice the default or only option |
| "Advanced users need flexibility" | Flexibility creates footguns; most "advanced" usage is copy-paste | Provide safe high-level APIs; hide primitives |
| "It's the developer's responsibility" | Blame-shifting; you designed the footgun | Remove the footgun or make it impossible to misuse |
| "Nobody would actually do that" | Developers do everything imaginable under pressure | Assume maximum developer confusion |
| "It's just a configuration option" | Config is code; wrong configs ship to production | Validate configs; reject dangerous combinations |
| "We need backwards compatibility" | Insecure defaults can't be grandfather-claused | Deprecate loudly; force migration |
| 合理化借口 | 错误原因 | 必要措施 |
|---|---|---|
| “有文档说明” | 开发者在截止日期压力下不会阅读文档 | 将安全选项设为默认或唯一选项 |
| “高级用户需要灵活性” | 灵活性会制造陷阱;大多数“高级”用法都是复制粘贴 | 提供安全的高层API;隐藏底层原语 |
| “这是开发者的责任” | 推卸责任;是你设计了陷阱 | 移除陷阱或使其无法被误用 |
| “没人真的会那么做” | 开发者在压力下会做出各种想象得到的操作 | 假设开发者会完全混淆 |
| “这只是一个配置选项” | 配置也是代码;错误的配置会被部署到生产环境 | 验证配置;拒绝危险组合 |
| “我们需要向后兼容性” | 不安全的默认值不能被“ grandfathered”(保留旧版本权限) | 大声宣布弃用;强制迁移 |
Sharp Edge Categories
尖锐边缘类别
1. Algorithm/Mode Selection Footguns
1. 算法/模式选择陷阱
APIs that let developers choose algorithms invite choosing wrong ones.
The JWT Pattern (canonical example):
- Header specifies algorithm: attacker can set to bypass signatures
"alg": "none" - Algorithm confusion: RSA public key used as HMAC secret when switching RS256→HS256
- Root cause: Letting untrusted input control security-critical decisions
Detection patterns:
- Function parameters like ,
algorithm,mode,cipherhash_type - Enums/strings selecting cryptographic primitives
- Configuration options for security mechanisms
Example - PHP password_hash allowing weak algorithms:
php
// DANGEROUS: allows crc32, md5, sha1
password_hash($password, PASSWORD_DEFAULT); // Good - no choice
hash($algorithm, $password); // BAD: accepts "crc32"允许开发者选择算法的API会导致他们选错。
JWT模式(典型示例):
- 头部指定算法:攻击者可以设置以绕过签名
"alg": "none" - 算法混淆:当从RS256切换到HS256时,RSA公钥被用作HMAC密钥
- 根本原因:让不可信输入控制安全关键决策
检测模式:
- 包含、
algorithm、mode、cipher等的函数参数hash_type - 选择加密原语的枚举/字符串
- 安全机制的配置选项
示例 - PHP password_hash允许弱算法:
php
// 危险:允许crc32、md5、sha1
password_hash($password, PASSWORD_DEFAULT); // 安全 - 无选择空间
hash($algorithm, $password); // 危险:接受"crc32"2. Dangerous Defaults
2. 危险默认值
Defaults that are insecure, or zero/empty values that disable security.
The OTP Lifetime Pattern:
python
undefined不安全的默认值,或会禁用安全功能的零/空值。
OTP生命周期模式:
python
undefinedWhat happens when lifetime=0?
当lifetime=0时会发生什么?
def verify_otp(code, lifetime=300): # 300 seconds default
if lifetime == 0:
return True # OOPS: 0 means "accept all"?
# Or does it mean "expired immediately"?
**Detection patterns:**
- Timeouts/lifetimes that accept 0 (infinite? immediate expiry?)
- Empty strings that bypass checks
- Null values that skip validation
- Boolean defaults that disable security features
- Negative values with undefined semantics
**Questions to ask:**
- What happens with `timeout=0`? `max_attempts=0`? `key=""`?
- Is the default the most secure option?
- Can any default value disable security entirely?def verify_otp(code, lifetime=300): # 默认300秒
if lifetime == 0:
return True # 糟糕:0意味着“接受所有”?
# 还是意味着“立即过期”?
**检测模式**:
- 接受0的超时/生命周期(无限?立即过期?)
- 绕过检查的空字符串
- 跳过验证的Null值
- 禁用安全功能的布尔默认值
- 语义未定义的负值
**需询问的问题:**
- `timeout=0`、`max_attempts=0`、`key=""`时会发生什么?
- 默认值是最安全的选项吗?
- 任何默认值是否会完全禁用安全功能?3. Primitive vs. Semantic APIs
3. 原语API vs 语义API
APIs that expose raw bytes instead of meaningful types invite type confusion.
The Libsodium vs. Halite Pattern:
php
// Libsodium (primitives): bytes are bytes
sodium_crypto_box($message, $nonce, $keypair);
// Easy to: swap nonce/keypair, reuse nonces, use wrong key type
// Halite (semantic): types enforce correct usage
Crypto::seal($message, new EncryptionPublicKey($key));
// Wrong key type = type error, not silent failureDetection patterns:
- Functions taking ,
bytes,stringfor distinct security concepts[]byte - Parameters that could be swapped without type errors
- Same type used for keys, nonces, ciphertexts, signatures
The comparison footgun:
go
// Timing-safe comparison looks identical to unsafe
if hmac == expected { } // BAD: timing attack
if hmac.Equal(mac, expected) { } // Good: constant-time
// Same types, different security properties暴露原始字节而非有意义类型的API会引发类型混淆。
Libsodium vs Halite模式:
php
// Libsodium(原语):字节就是字节
sodium_crypto_box($message, $nonce, $keypair);
// 容易出现:交换nonce/密钥对、重用nonce、使用错误密钥类型
// Halite(语义):类型强制正确用法
Crypto::seal($message, new EncryptionPublicKey($key));
// 错误密钥类型 = 类型错误,而非静默失败检测模式:
- 为不同安全概念接受、
bytes、string的函数[]byte - 无需类型错误即可交换的参数
- 密钥、nonce、密文、签名使用相同类型
比较陷阱:
go
// 计时安全的比较与不安全的比较看起来相同
if hmac == expected { } // 危险:计时攻击
if hmac.Equal(mac, expected) { } // 安全:恒定时间
// 相同类型,不同安全属性4. Configuration Cliffs
4. 配置悬崖
One wrong setting creates catastrophic failure, with no warning.
Detection patterns:
- Boolean flags that disable security entirely
- String configs that aren't validated
- Combinations of settings that interact dangerously
- Environment variables that override security settings
- Constructor parameters with sensible defaults but no validation (callers can override with insecure values)
Examples:
yaml
undefined一个错误设置会导致灾难性故障,且无任何警告。
检测模式:
- 完全禁用安全功能的布尔标志
- 未验证的字符串配置
- 交互危险的设置组合
- 覆盖安全设置的环境变量
- 具有合理默认值但未验证的构造函数参数(调用者可以用不安全值覆盖)
示例:
yaml
undefinedOne typo = disaster
一个拼写错误 = 灾难
verify_ssl: fasle # Typo silently accepted as truthy?
verify_ssl: fasle # 拼写错误是否会被当作真值接受?
Magic values
魔法值
session_timeout: -1 # Does this mean "never expire"?
session_timeout: -1 # 这是否意味着“永不过期”?
Dangerous combinations accepted silently
危险组合被静默接受
auth_required: true
bypass_auth_for_health_checks: true
health_check_path: "/" # Oops
```php
// Sensible default doesn't protect against bad callers
public function __construct(
public string $hashAlgo = 'sha256', // Good default...
public int $otpLifetime = 120, // ...but accepts md5, 0, etc.
) {}See config-patterns.md for detailed patterns.
auth_required: true
bypass_auth_for_health_checks: true
health_check_path: "/" # 糟糕
```php5. Silent Failures
合理的默认值无法防范错误的调用者
Errors that don't surface, or success that masks failure.
Detection patterns:
- Functions returning booleans instead of throwing on security failures
- Empty catch blocks around security operations
- Default values substituted on parse errors
- Verification functions that "succeed" on malformed input
Examples:
python
undefinedpublic function __construct(
public string $hashAlgo = 'sha256', // 好的默认值...
public int $otpLifetime = 120, // ...但接受md5、0等
) {}
详见[config-patterns.md](references/config-patterns.md#unvalidated-constructor-parameters)获取详细模式。Silent bypass
5. 静默失败
def verify_signature(sig, data, key):
if not key:
return True # No key = skip verification?!
错误不显现,或成功掩盖了失败。
检测模式:
- 安全失败时返回布尔值而非抛出异常的函数
- 安全操作周围的空catch块
- 解析错误时代入默认值
- 对畸形输入“成功”验证的函数
示例:
python
undefinedReturn value ignored
静默绕过
signature.verify(data, sig) # Throws on failure
crypto.verify(data, sig) # Returns False on failure
def verify_signature(sig, data, key):
if not key:
return True # 无密钥 = 跳过验证?!
Developer forgets to check return value
返回值被忽略
undefinedsignature.verify(data, sig) # 失败时抛出异常
crypto.verify(data, sig) # 失败时返回False
6. Stringly-Typed Security
开发者忘记检查返回值
Security-critical values as plain strings enable injection and confusion.
Detection patterns:
- SQL/commands built from string concatenation
- Permissions as comma-separated strings
- Roles/scopes as arbitrary strings instead of enums
- URLs constructed by joining strings
The permission accumulation footgun:
python
permissions = "read,write"
permissions += ",admin" # Too easy to escalateundefinedvs. type-safe
6. 字符串化安全
permissions = {Permission.READ, Permission.WRITE}
permissions.add(Permission.ADMIN) # At least it's explicit
undefined安全关键值作为普通字符串会导致注入和混淆。
检测模式:
- 通过字符串拼接构建SQL/命令
- 权限作为逗号分隔的字符串
- 角色/范围作为任意字符串而非枚举
- 通过拼接字符串构造URL
权限累积陷阱:
python
permissions = "read,write"
permissions += ",admin" # 太容易升级权限Analysis Workflow
vs. 类型安全
Phase 1: Surface Identification
—
- Map security-relevant APIs: authentication, authorization, cryptography, session management, input validation
- Identify developer choice points: Where can developers select algorithms, configure timeouts, choose modes?
- Find configuration schemas: Environment variables, config files, constructor parameters
permissions = {Permission.READ, Permission.WRITE}
permissions.add(Permission.ADMIN) # 至少是显式的
undefinedPhase 2: Edge Case Probing
分析流程
—
阶段1:表面识别
For each choice point, ask:
- Zero/empty/null: What happens with ,
0,"",null?[] - Negative values: What does mean? Infinite? Error?
-1 - Type confusion: Can different security concepts be swapped?
- Default values: Is the default secure? Is it documented?
- Error paths: What happens on invalid input? Silent acceptance?
- 映射安全相关API:认证、授权、加密、会话管理、输入验证
- 识别开发者选择点:开发者可以选择算法、配置超时、选择模式的位置?
- 查找配置架构:环境变量、配置文件、构造函数参数
Phase 3: Threat Modeling
阶段2:边缘情况探查
Consider three adversaries:
-
The Scoundrel: Actively malicious developer or attacker controlling config
- Can they disable security via configuration?
- Can they downgrade algorithms?
- Can they inject malicious values?
-
The Lazy Developer: Copy-pastes examples, skips documentation
- Will the first example they find be secure?
- Is the path of least resistance secure?
- Do error messages guide toward secure usage?
-
The Confused Developer: Misunderstands the API
- Can they swap parameters without type errors?
- Can they use the wrong key/algorithm/mode by accident?
- Are failure modes obvious or silent?
对于每个选择点,询问:
- 零/空/Null:、
0、""、null会发生什么?[] - 负值:意味着什么?无限?错误?
-1 - 类型混淆:不同安全概念是否可以被交换?
- 默认值:默认值是否安全?是否有文档说明?
- 错误路径:输入无效时会发生什么?静默接受?
Phase 4: Validate Findings
阶段3:威胁建模
For each identified sharp edge:
- Reproduce the misuse: Write minimal code demonstrating the footgun
- Verify exploitability: Does the misuse create a real vulnerability?
- Check documentation: Is the danger documented? (Documentation doesn't excuse bad design, but affects severity)
- Test mitigations: Can the API be used safely with reasonable effort?
If a finding seems questionable, return to Phase 2 and probe more edge cases.
考虑三类攻击者:
-
恶棍:主动恶意的开发者或控制配置的攻击者
- 他们能否通过配置禁用安全功能?
- 他们能否降级算法?
- 他们能否注入恶意值?
-
懒惰开发者:复制粘贴示例,跳过文档
- 他们找到的第一个示例是否安全?
- 阻力最小的路径是否安全?
- 错误消息是否引导他们使用安全用法?
-
困惑开发者:误解API
- 他们能否无类型错误地交换参数?
- 他们是否会意外使用错误的密钥/算法/模式?
- 失败模式是明显的还是静默的?
Severity Classification
阶段4:验证发现
| Severity | Criteria | Examples |
|---|---|---|
| Critical | Default or obvious usage is insecure | |
| High | Easy misconfiguration breaks security | Algorithm parameter accepts "none" |
| Medium | Unusual but possible misconfiguration | Negative timeout has unexpected meaning |
| Low | Requires deliberate misuse | Obscure parameter combination |
对于每个识别出的尖锐边缘:
- 重现误用:编写最小化代码演示陷阱
- 验证可利用性:误用是否会造成真实漏洞?
- 检查文档:危险是否有文档说明?(文档不能弥补糟糕的设计,但会影响严重程度)
- 测试缓解措施:API能否通过合理努力安全使用?
如果发现有疑问,返回阶段2探查更多边缘情况。
References
严重程度分类
By category:
- Cryptographic APIs: See references/crypto-apis.md
- Configuration Patterns: See references/config-patterns.md
- Authentication/Session: See references/auth-patterns.md
- Real-World Case Studies: See references/case-studies.md (OpenSSL, GMP, etc.)
By language (general footguns, not crypto-specific):
| Language | Guide |
|---|---|
| C/C++ | references/lang-c.md |
| Go | references/lang-go.md |
| Rust | references/lang-rust.md |
| Swift | references/lang-swift.md |
| Java | references/lang-java.md |
| Kotlin | references/lang-kotlin.md |
| C# | references/lang-csharp.md |
| PHP | references/lang-php.md |
| JavaScript/TypeScript | references/lang-javascript.md |
| Python | references/lang-python.md |
| Ruby | references/lang-ruby.md |
See also references/language-specific.md for a combined quick reference.
| 严重程度 | 标准 | 示例 |
|---|---|---|
| 关键 | 默认或明显用法不安全 | |
| 高 | 容易的错误配置会破坏安全 | 算法参数接受"none" |
| 中 | 不常见但可能的错误配置 | 负超时具有意外语义 |
| 低 | 需要故意误用 | 模糊的参数组合 |
Quality Checklist
参考资料
Before concluding analysis:
- Probed all zero/empty/null edge cases
- Verified defaults are secure
- Checked for algorithm/mode selection footguns
- Tested type confusion between security concepts
- Considered all three adversary types
- Verified error paths don't bypass security
- Checked configuration validation
- Constructor params validated (not just defaulted) - see config-patterns.md
按类别:
- 加密API:详见references/crypto-apis.md
- 配置模式:详见references/config-patterns.md
- 认证/会话:详见references/auth-patterns.md
- 真实世界案例研究:详见references/case-studies.md(OpenSSL、GMP等)
按语言(通用陷阱,非加密特定):
| 语言 | 指南 |
|---|---|
| C/C++ | references/lang-c.md |
| Go | references/lang-go.md |
| Rust | references/lang-rust.md |
| Swift | references/lang-swift.md |
| Java | references/lang-java.md |
| Kotlin | references/lang-kotlin.md |
| C# | references/lang-csharp.md |
| PHP | references/lang-php.md |
| JavaScript/TypeScript | references/lang-javascript.md |
| Python | references/lang-python.md |
| Ruby | references/lang-ruby.md |
另请参阅references/language-specific.md获取组合快速参考。
—
质量检查清单
—
结束分析前:
- 探查了所有零/空/Null边缘情况
- 验证了默认值是安全的
- 检查了算法/模式选择陷阱
- 测试了安全概念之间的类型混淆
- 考虑了所有三类攻击者
- 验证了错误路径不会绕过安全
- 检查了配置验证
- 构造函数参数已验证(不仅仅是默认值)- 详见config-patterns.md ",