sharp-edges について
API、設定、インターフェースが開発者の誤用に耐えられるかを評価するセキュリティ分析スキルです。4段階の手順で危険なデフォルト、境界条件、設定上の落とし穴、失敗時の挙動を調べ、専用エージェントによる自律分析にも対応します。
主な機能
- 014段階の分析ワークフロー
- 02危険なデフォルトと設定上の落とし穴の確認
- 03境界条件と型の混同の検証
- 04開発者の誤用を想定した脅威分析
- 05言語別・セキュリティ分野別の参考資料
- 06自律分析エージェント
活用シーン
- 01セキュリティに関わる API 設計のレビュー
- 02設定スキーマとデフォルト値の監査
- 03暗号 API の使いやすさの評価
- 04認証・認可インターフェースの検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trailofbits/ skills --skill sharp-edgesインストール後、Claude Code で/sharp-edgesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlassets/trail-of-bits-mark.svgreferences/auth-patterns.mdcase-studies.mdconfig-patterns.mdcrypto-apis.mdlang-c.mdlang-csharp.mdlang-go.mdlang-java.mdlang-javascript.mdlang-kotlin.mdlang-php.mdlang-python.mdlang-ruby.mdlang-rust.mdlang-swift.mdlanguage-specific.md詳細
シャープエッジ分析
API、設定、インターフェースが、開発者による誤用にどれだけ強いかを評価します。「簡単な方法」を選ぶと安全性が損なわれる設計を特定します。
使用する場面
- API やライブラリの設計上の判断をレビューするとき
- 危険なオプションがないか設定スキーマを監査するとき
- 暗号 API の使いやすさを評価するとき
- 認証・認可インターフェースを評価するとき
- セキュリティに関わる選択肢を開発者に公開するコードをレビューするとき
使用しない場面
- 実装上のバグ(標準的なコードレビューを使用してください)
- ビジネスロジックの欠陥(ドメイン固有の分析を使用してください)
- パフォーマンスの最適化(別の観点です)
エージェント
sharp-edges-analyzer エージェントは、シャープエッジ分析のワークフロー全体を自律的に実行します。API、設定、インターフェースについて、誤用への耐性や危険な落とし穴の可能性を専門的に分析したい場合に使用してください。このエージェントは、4 段階のワークフロー(サーフェスの特定、エッジケースの調査、脅威モデリング、検出結果の検証)に従い、必要に応じて言語固有のリファレンスを参照します。
基本原則
成功への道筋:安全な使い方が最も抵抗の少ない道筋であるべきです。開発者が脆弱性を避けるために暗号技術を理解したり、ドキュメントを注意深く読んだり、特別なルールを覚えたりする必要があるなら、その 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を受け取る関数 - 型エラーを起こさずに入れ替えられるパラメーター
- 鍵、ノンス、暗号文、署名に同じ型が使われている
比較時の落とし穴:
// 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. 設定の落とし穴
設定を1つ間違えると、警告もなく致命的な障害が発生します。
検出パターン:
- セキュリティを完全に無効化するブール値フラグ
- 検証されない文字列設定
- 危険な相互作用を引き起こす設定の組み合わせ
- セキュリティ設定を上書きする環境変数
- 妥当なデフォルト値はあるものの、検証のないコンストラクター引数(呼び出し側が安全でない値に上書きできます)
例:
# 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:表面の特定
- セキュリティに関連する API を洗い出す:認証、認可、暗号化、セッション管理、入力検証
- 開発者が選択する箇所を特定する:開発者がアルゴリズムを選んだり、タイムアウトを設定したり、モードを選択したりできる箇所はどこですか?
- 設定スキーマを見つける:環境変数、設定ファイル、コンストラクター引数
フェーズ 2:エッジケースの調査
選択箇所ごとに、次の点を確認します。
- ゼロ/空/null:
0、""、null、[]の場合、どうなりますか? - 負の値:
-1は何を意味しますか?無限ですか?エラーですか? - 型の混同:異なるセキュリティ概念を入れ替えられますか?
- デフォルト値:デフォルト値は安全ですか?ドキュメントに記載されていますか?
- エラーパス:無効な入力の場合、どうなりますか?黙って受け入れられますか?
フェーズ 3:脅威モデリング
次の3種類の攻撃者を考慮します。
-
悪意ある開発者:設定を操作できる、積極的に悪意を持つ開発者または攻撃者
- 設定によってセキュリティを無効化できますか?
- アルゴリズムをダウングレードできますか?
- 悪意のある値を注入できますか?
-
怠惰な開発者:サンプルをコピー&ペーストし、ドキュメントを読み飛ばします
- 最初に見つけるサンプルは安全ですか?
- 最も手軽な方法は安全ですか?
- エラーメッセージは安全な使い方へ導きますか?
-
混乱した開発者:API を誤解しています
- 型エラーなしで引数を入れ替えられますか?
- 間違ったキー/アルゴリズム/モードを誤って使用できますか?
- 失敗の仕方は明白ですか、それともサイレントですか?
フェーズ 4:調査結果の検証
特定した危険な箇所ごとに、次の手順を行います。
- 誤用を再現する:危険な使い方を示す最小限のコードを書く
- 悪用可能性を確認する:その誤用によって実際の脆弱性が生じますか?
- ドキュメントを確認する:危険性はドキュメントに記載されていますか?(ドキュメントに記載があっても設計の問題は解消されませんが、深刻度には影響します)
- 緩和策をテストする:妥当な労力で API を安全に使用できますか?
調査結果に疑問がある場合は、フェーズ 2 に戻り、さらにエッジケースを調査してください。
深刻度の分類
| 深刻度 | 基準 | 例 |
|---|---|---|
| 致命的 | デフォルトまたは明白な使い方が安全でない | verify: false がデフォルト、空のパスワードが許可される |
| 高 | 簡単な設定ミスでセキュリティが損なわれる | アルゴリズム引数が「none」を受け入れる |
| 中 | 一般的ではないが、起こり得る設定ミス | 負のタイムアウト値が予期しない意味を持つ |
| 低 | 意図的な誤用が必要 | 分かりにくい引数の組み合わせ |
参考資料
カテゴリ別:
- 暗号 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 のすべてのエッジケースを調査した
- デフォルト設定が安全であることを確認した
- アルゴリズム/モード選択に関する落とし穴を確認した
- セキュリティの概念間での型の取り違えをテストした
- 3 種類すべての敵対者を考慮した
- エラー処理経路がセキュリティを回避しないことを確認した
- 設定の検証を確認した
- コンストラクターのパラメーターを検証した(デフォルト値を設定するだけでは不十分)— config-patterns.md を参照