sharp-edges

sharp-edges

熱門

識別容易出錯的 API、危險設定以及會導致安全失誤的陷阱設計。適用於審查 API 設計、設定架構、密碼學函式庫易用性,或評估程式碼是否遵循「預設安全」與「成功之坑」原則。觸發詞:footgun、misuse-resistant、secure defaults、API usability、dangerous configuration。

6276星標
542分支
更新於 2026/7/20
SKILL.md
唯讀
名稱
sharp-edges
描述

識別容易出錯的 API、危險設定以及會導致安全失誤的陷阱設計。適用於審查 API 設計、設定架構、密碼學函式庫易用性,或評估程式碼是否遵循「預設安全」與「成功之坑」原則。觸發詞:footgun、misuse-resistant、secure defaults、API usability、dangerous configuration。

尖銳邊緣分析

評估 API、設定與介面是否能夠抵抗開發者的誤用。找出那些「簡單路徑」反而導致不安全的情況。

使用時機

  • 審查 API 或函式庫的設計決策
  • 稽核設定架構中是否有危險選項
  • 評估密碼學 API 的易用性
  • 評估認證/授權介面
  • 審查任何讓開發者接觸到安全相關選擇的程式碼

不適用時機

  • 實作錯誤(請使用標準程式碼審查)
  • 業務邏輯缺陷(請使用領域特定分析)
  • 效能最佳化(屬於不同範疇)

Agent

sharp-edges-analyzer agent 會自動執行完整的尖銳邊緣分析流程。當你需要針對 API、設定或介面進行誤用抵抗性與陷阱潛力的專門分析時,可以使用它。此 agent 遵循四個階段的工作流程(表面識別、邊界案例探測、威脅建模、驗證發現),並會按需讀取語言特定的參考資料。

核心原則

成功之坑:安全的使用方式應該是阻力最小的路徑。如果開發者必須理解密碼學、仔細閱讀文件或記住特殊規則才能避免漏洞,那麼這個 API 就是失敗的。

應拒絕的合理化藉口

合理化藉口 為什麼是錯的 應採取的行動
「文件有寫」 開發者在期限壓力下不會讀文件 讓安全的選擇成為預設或唯一選項
「進階使用者需要彈性」 彈性會製造陷阱;大多數「進階」用法只是複製貼上 提供安全的高階 API;隱藏底層實作
「這是開發者的責任」 推卸責任;是你設計了陷阱 移除陷阱或讓它不可能被誤用
「沒人會真的那樣做」 開發者在壓力下什麼事都做得出來 假設開發者會極度困惑
「那只是一個設定選項」 設定就是程式碼;錯誤的設定會上線到正式環境 驗證設定;拒絕危險的組合
「我們需要向後相容」 不安全的預設值不能因為相容性而保留 強烈標記為棄用;強制遷移

尖銳邊緣類別

1. 演算法/模式選擇陷阱

讓開發者選擇演算法的 API 容易導致選錯演算法。

JWT 模式(典型範例):

  • Header 指定演算法:攻擊者可設定 "alg": "none" 繞過簽章
  • 演算法混淆:當從 RS256 切換到 HS256 時,RSA 公鑰被當作 HMAC 密鑰使用
  • 根本原因:讓不受信任的輸入控制安全關鍵決策

偵測模式:

  • 函式參數如 algorithmmodecipherhash_type
  • 選擇密碼學原語的列舉/字串
  • 安全機制的設定選項

範例 - PHP password_hash 允許弱演算法:

// 危險:允許 crc32、md5、sha1
password_hash($password, PASSWORD_DEFAULT); // 好 - 沒有選擇
hash($algorithm, $password); // 壞:接受 "crc32"

2. 危險的預設值

不安全的預設值,或零值/空值會停用安全機制。

OTP 生命週期模式:

# 當 lifetime=0 時會發生什麼事?
def verify_otp(code, lifetime=300):  # 預設 300 秒
    if lifetime == 0:
        return True  # 哎呀:0 表示「接受所有」?
        # 還是表示「立即過期」?

偵測模式:

  • 接受 0 的超時/生命週期(無限?立即過期?)
  • 繞過檢查的空字串
  • 跳過驗證的 null 值
  • 停用安全功能的布林預設值
  • 語義未定義的負數值

要問的問題:

  • timeout=0max_attempts=0key="" 時會發生什麼事?
  • 預設值是否為最安全的選項?
  • 是否有任何預設值可以完全停用安全機制?

3. 原始 vs. 語義 API

暴露原始位元組而非有意義型別的 API 容易導致型別混淆。

Libsodium vs. Halite 模式:

// Libsodium(原始):位元組就是位元組
sodium_crypto_box($message, $nonce, $keypair);
// 容易:互換 nonce/keypair、重複使用 nonce、使用錯誤的密鑰類型

// Halite(語義):型別強制正確使用
Crypto::seal($message, new EncryptionPublicKey($key));
// 錯誤的密鑰類型 = 型別錯誤,而非靜默失敗

偵測模式:

  • 對不同的安全概念使用 bytesstring[]byte 的函式
  • 參數可能被互換而不會產生型別錯誤
  • 密鑰、nonce、密文、簽章使用相同型別

比較陷阱:

// 時間安全比較看起來與不安全比較一模一樣
if hmac == expected { }           // 壞:計時攻擊
if hmac.Equal(mac, expected) { }  // 好:常數時間
// 相同型別,不同的安全屬性

4. 設定懸崖

一個錯誤的設定就會造成災難性失敗,而且沒有任何警告。

偵測模式:

  • 完全停用安全機制的布林旗標
  • 未經驗證的字串設定
  • 會產生危險互動的設定組合
  • 覆蓋安全設定的環境變數
  • 有合理預設值但未經驗證的建構子參數(呼叫者可以傳入不安全的值)

範例:

# 一個拼寫錯誤 = 災難
verify_ssl: fasle  # 拼寫錯誤被靜默接受為 truthy?

# 魔術值
session_timeout: -1  # 這表示「永不過期」嗎?

# 危險組合被靜默接受
auth_required: true
bypass_auth_for_health_checks: true
health_check_path: "/"  # 哎呀
// 合理的預設值無法防止不良的呼叫者
public function __construct(
    public string $hashAlgo = 'sha256',  // 好的預設值...
    public int $otpLifetime = 120,       // ...但接受 md5、0 等
) {}

詳細模式請參閱 config-patterns.md

5. 靜默失敗

沒有浮現的錯誤,或成功掩蓋了失敗。

偵測模式:

  • 回傳布林值而非在安全失敗時拋出例外
  • 安全操作周圍的空 catch 區塊
  • 解析錯誤時替換為預設值
  • 對格式錯誤的輸入「成功」的驗證函式

範例:

# 靜默繞過
def verify_signature(sig, data, key):
    if not key:
        return True  # 沒有密鑰 = 跳過驗證?!

# 忽略回傳值
signature.verify(data, sig)  # 失敗時拋出例外
crypto.verify(data, sig)     # 失敗時回傳 False
# 開發者忘記檢查回傳值

6. 字串型安全

將安全關鍵值以純字串形式處理會導致注入與混淆。

偵測模式:

  • 透過字串串接建立的 SQL/命令
  • 以逗號分隔字串表示的權限
  • 以任意字串而非列舉表示的角色/範圍
  • 透過字串串接建構的 URL

權限累積陷阱:

permissions = "read,write"
permissions += ",admin"  # 太容易升級了

# vs. 型別安全
permissions = {Permission.READ, Permission.WRITE}
permissions.add(Permission.ADMIN)  # 至少是明確的

分析工作流程

第一階段:表面識別

  1. 繪製安全相關 API 地圖:認證、授權、密碼學、session 管理、輸入驗證
  2. 識別開發者選擇點:開發者可以在哪些地方選擇演算法、設定超時、選擇模式?
  3. 尋找設定架構:環境變數、設定檔、建構子參數

第二階段:邊界案例探測

對每個選擇點,問:

  • 零/空/null0""null[] 時會發生什麼事?
  • 負數值-1 是什麼意思?無限?錯誤?
  • 型別混淆:不同的安全概念可以被互換嗎?
  • 預設值:預設值安全嗎?有文件說明嗎?
  • 錯誤路徑:無效輸入時會發生什麼事?靜默接受?

第三階段:威脅建模

考慮三種對手:

  1. 惡徒:惡意開發者或控制設定的攻擊者

    • 他們能透過設定停用安全機制嗎?
    • 他們能降級演算法嗎?
    • 他們能注入惡意值嗎?
  2. 懶惰開發者:複製貼上範例,跳過文件

    • 他們找到的第一個範例是否安全?
    • 阻力最小的路徑是否安全?
    • 錯誤訊息是否引導他們使用安全的方式?
  3. 困惑的開發者:誤解 API

    • 他們能在不產生型別錯誤的情況下互換參數嗎?
    • 他們會意外使用錯誤的密鑰/演算法/模式嗎?
    • 失敗模式是明顯的還是靜默的?

第四階段:驗證發現

對每個識別出的尖銳邊緣:

  1. 重現誤用:撰寫最小程式碼展示陷阱
  2. 驗證可利用性:誤用是否會造成真實漏洞?
  3. 檢查文件:危險是否有文件說明?(文件不能成為不良設計的藉口,但會影響嚴重程度)
  4. 測試緩解措施:API 能否以合理的努力安全使用?

如果某個發現看起來有問題,回到第二階段並探測更多邊界案例。

嚴重程度分類

嚴重程度 標準 範例
嚴重 預設或明顯的使用方式不安全 verify: false 為預設值;允許空密碼
簡單的錯誤設定就會破壞安全 演算法參數接受 "none"
不常見但可能的錯誤設定 負數超時有意外含義
需要刻意誤用 晦澀的參數組合

參考資料

依類別:

依語言(一般陷阱,非密碼學特定):

語言 指南
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