mcpField
S
sharp-edges

sharp-edges

github.com/trailofbits安全7.9k7.0k 次安装今天更新
代码仓库

关于 sharp-edges

用于评估 API、配置和接口能否抵御开发者误用的安全分析技能。它通过四阶段流程检查不安全默认值、边界情况、配置陷阱和失败行为;专用 Agent 可自主执行分析。

主要功能

  1. 01四阶段分析流程
  2. 02检查危险默认值与配置陷阱
  3. 03探查边界情况和类型混淆
  4. 04针对开发者误用进行威胁建模
  5. 05按语言和安全类别提供参考
  6. 06自主分析 Agent

适用场景

  1. 01审查安全敏感的 API 设计
  2. 02审计配置模式与默认值
  3. 03评估加密 API 的易用性
  4. 04检查认证与授权接口

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trailofbits/skills --skill sharp-edges

安装后在 Claude Code 中输入/sharp-edges确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
assets/
trail-of-bits-mark.svg
references/
auth-patterns.md
case-studies.md
config-patterns.md
crypto-apis.md
lang-c.md
lang-csharp.md
lang-go.md
lang-java.md
lang-javascript.md
lang-kotlin.md
lang-php.md
lang-python.md
lang-ruby.md
lang-rust.md
lang-swift.md
language-specific.md

详细介绍

锐边分析

评估 API、配置和接口是否能抵御开发者误用。识别那些会让「最简单的做法」导致安全问题的设计。

适用场景

  • 审查 API 或库的设计决策
  • 审计包含危险选项的配置架构
  • 评估加密 API 的易用性
  • 评估身份验证/授权接口
  • 审查任何向开发者开放安全相关选项的代码

不适用场景

  • 实现缺陷(使用标准代码审查)
  • 业务逻辑漏洞(使用特定领域分析)
  • 性能优化(这是另一个关注点)

Agent

sharp-edges-analyzer Agent 会自主运行完整的锐边分析工作流。如果你需要专门分析 API、配置或接口的抗误用能力和潜在陷阱,请使用该 Agent。它会遵循四阶段工作流(识别表面、探查边缘情况、威胁建模、验证发现),并按需查阅特定语言的参考资料。

核心原则

成功之路:安全的用法应该是阻力最小的路径。如果开发者必须理解密码学、仔细阅读文档或记住特殊规则才能避免漏洞,那么这个 API 就失败了。

应当驳斥的托词

托词为什么不对必须采取的行动
「有文档说明」开发者在截止期限的压力下不会阅读文档将安全选项设为默认选项或唯一选项
「高级用户需要灵活性」灵活性会制造陷阱;多数所谓的「高级」用法都是复制粘贴提供安全的高级 API;隐藏底层原语
「这是开发者的责任」推卸责任;陷阱是你设计的移除陷阱,或让误用成为不可能
「没人会真的那样做」开发者在压力下什么都做得出来假设开发者会陷入最大的困惑
「这只是一个配置选项」配置就是代码;错误配置会被部署到生产环境验证配置;拒绝危险的组合
「我们需要向后兼容」不能把不安全的默认值当作祖传包袱明确弃用;强制迁移

锐边类别

1. 算法/模式选择陷阱

允许开发者选择算法的 API,会诱使他们选错算法。

JWT 模式(典型示例):

  • 标头指定算法:攻击者可以设置 "alg": "none" 来绕过签名
  • 算法混淆:切换 RS256→HS256 时,将 RSA 公钥用作 HMAC 密钥
  • 根本原因:让不可信输入控制安全关键决策

检测模式:

  • algorithm、mode、cipher、hash_type 等函数参数
  • 用于选择加密原语的枚举/字符串
  • 安全机制的配置选项

示例——允许使用弱算法的 PHP password_hash:

// DANGEROUS: allows crc32, md5, sha1
password_hash($password, PASSWORD_DEFAULT); // Good - no choice
hash($algorithm, $password); // BAD: accepts "crc32"
2. 危险的默认值

默认值不安全,或零值/空值会禁用安全机制。

OTP 有效期模式:

# What happens when 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"?

检测模式:

  • 接受 0 的超时/有效期(无限期?立即过期?)
  • 绕过检查的空字符串
  • 跳过验证的 null 值
  • 会禁用安全功能的布尔默认值
  • 语义未定义的负值

需要提出的问题:

  • timeout=0、max_attempts=0、key="" 会怎样?
  • 默认值是否是最安全的选项?
  • 是否有任何默认值会彻底禁用安全机制?
3. 原语 API 与语义化 API

暴露原始字节而非有意义类型的 API,会导致类型混淆。

Libsodium 与 Halite 模式:

// 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 failure

检测模式:

  • 使用 bytes、string、[]byte 接收不同安全概念的函数
  • 不同参数互换后不会触发类型错误
  • 密钥、nonce、密文和签名使用相同类型

比较陷阱:

// 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
4. 配置陷阱

一个错误的设置就会导致灾难性故障,而且不会发出警告。

检测模式:

  • 会彻底禁用安全功能的布尔标志
  • 未经验证的字符串配置
  • 会相互作用并造成危险后果的设置组合
  • 会覆盖安全设置的环境变量
  • 默认值合理但未经验证的构造函数参数(调用方可以将其覆盖为不安全的值)

示例:

# One typo = disaster
verify_ssl: fasle  # Typo silently accepted as truthy?

# Magic values
session_timeout: -1  # Does this mean "never expire"?

# Dangerous combinations accepted silently
auth_required: true
bypass_auth_for_health_checks: true
health_check_path: "/"  # Oops
// 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.
) {}

有关详细模式,请参阅 config-patterns.md。

5. 静默失败

错误没有显现出来,或成功掩盖了失败。

检测模式:

  • 安全操作失败时返回布尔值而非抛出异常的函数
  • 安全操作周围的空 catch 块
  • 解析错误时替换为默认值
  • 在输入格式错误时仍然“成功”的验证函数

示例:

# Silent bypass
def verify_signature(sig, data, key):
    if not key:
        return True  # No key = skip verification?!

# Return value ignored
signature.verify(data, sig)  # Throws on failure
crypto.verify(data, sig)     # Returns False on failure
# Developer forgets to check return value
6. 字符串化安全

将对安全至关重要的值设为普通字符串,会引发注入和混淆问题。

检测模式:

  • 通过字符串拼接构造 SQL/命令
  • 用逗号分隔的字符串表示权限
  • 将角色/作用域设为任意字符串,而非枚举
  • 通过拼接字符串构造 URL

权限累积陷阱:

permissions = "read,write"
permissions += ",admin"  # Too easy to escalate

# vs. type-safe
permissions = {Permission.READ, Permission.WRITE}
permissions.add(Permission.ADMIN)  # At least it's explicit

分析工作流

阶段 1:识别表面
  1. 梳理与安全相关的 API:身份验证、授权、加密、会话管理、输入验证
  2. 确定开发者的选择点:开发者可以在哪里选择算法、配置超时、选择模式?
  3. 查找配置架构:环境变量、配置文件、构造函数参数
阶段 2:探查边界情况

针对每个选择点,询问:

  • 零/空/null:0、""、null、[] 会有什么结果?
  • 负值:-1 表示什么?无限?错误?
  • 类型混淆:不同的安全概念能否互换?
  • 默认值:默认值是否安全?是否有文档说明?
  • 错误路径:输入无效时会发生什么?是否会静默接受?
阶段 3:威胁建模

考虑三类对手:

  1. 恶意开发者:主动恶意的开发者,或能控制配置的攻击者

    • 他们能否通过配置禁用安全功能?
    • 他们能否降级算法?
    • 他们能否注入恶意值?
  2. 懒惰的开发者:复制粘贴示例,跳过文档

    • 他们找到的第一个示例是否安全?
    • 最省力的做法是否安全?
    • 错误消息是否会引导他们安全使用?
  3. 困惑的开发者:误解 API

    • 他们能否在不触发类型错误的情况下互换参数?
    • 他们能否意外使用错误的密钥/算法/模式?
    • 失败情况是否明显,还是会静默发生?
阶段 4:验证发现

针对每个已识别的陷阱:

  1. 复现误用:编写最精简的代码来演示该陷阱
  2. 验证可利用性:该误用是否会造成真实漏洞?
  3. 检查文档:是否有文档说明危险?(文档说明不能弥补糟糕的设计,但会影响严重程度)
  4. 测试缓解措施:能否以合理的成本安全地使用该 API?

如果某项发现似乎有疑问,请返回阶段 2,进一步探查边界情况。

严重程度分类

严重程度标准示例
严重默认或明显的用法不安全默认值为 verify: false;允许空密码
高容易因配置错误而破坏安全性算法参数接受 "none"
中不常见但可能出现的配置错误负超时值的含义出乎意料
低需要故意误用隐晦的参数组合

参考资料

按类别:

按语言(通用易错点,与加密无关):

语言指南
C/C++references/lang-c.md
Goreferences/lang-go.md
Rustreferences/lang-rust.md
Swiftreferences/lang-swift.md
Javareferences/lang-java.md
Kotlinreferences/lang-kotlin.md
C#references/lang-csharp.md
PHPreferences/lang-php.md
JavaScript/TypeScriptreferences/lang-javascript.md
Pythonreferences/lang-python.md
Rubyreferences/lang-ruby.md

另请参见 references/language-specific.md,获取合并的快速参考。

质量检查清单

结束分析前:

  • 探测所有零值/空值/null 边界情况
  • 验证默认设置安全
  • 检查算法/模式选择中的易错点
  • 测试安全概念之间的类型混淆
  • 考虑全部三类对手
  • 验证错误处理路径不会绕过安全措施
  • 检查配置验证
  • 验证构造函数参数(而非仅设置默认值)——参见 config-patterns.md

评论

支持 Markdown