sharp-edges について
sharp-edgesは、API、設定、インターフェースが安全に使いやすいかを評価するセキュリティレビュー用スキルです。安全でないデフォルト、型の混同、エラーの隠蔽などの誤用リスクに着目し、体系的な検証手順を示します。
主な機能
- 01APIと設定の誤用リスクをレビュー
- 02危険なデフォルトと設定上の落とし穴を特定
- 03境界値と失敗経路を調査
- 04誤用例で指摘事項を検証
- 05重大度分類と品質チェックリスト
活用シーン
- 01暗号APIの開発者向け設計をレビュー
- 02認証・認可インターフェースを評価
- 03設定項目の危険な組み合わせを監査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ waybarrios/ opencode-power-pack --skill sharp-edgesインストール後、Claude Code で/sharp-edgesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/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 の使いやすさを評価する
- 認証・認可インターフェースを評価する
- セキュリティに関わる選択肢を開発者に公開するコードをレビューする
使用しない場面
- 実装上のバグ(通常のコードレビューを使用)
- ビジネスロジックの欠陥(ドメイン固有の分析を使用)
- パフォーマンスの最適化(別の観点)
基本原則
成功への道筋:安全な使い方が、最も抵抗の少ない道であるべきです。脆弱性を避けるために、開発者が暗号技術を理解したり、ドキュメントを注意深く読んだり、特別なルールを覚えたりしなければならないなら、その 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 を参照してください