security-and-hardening について
Webアプリを開発し、ユーザーデータや認証、外部システムを扱う開発者向けのセキュリティ強化スキルです。脅威モデリングに加え、入力検証、アクセス制御、プライバシー、依存関係、LLM機能の安全策を扱います。
主な機能
- 01STRIDEによる脅威モデリング
- 02入力検証とアクセス制御
- 03認証とデータ保護
- 04依存関係とサプライチェーンの確認
- 05プライバシーと保持期間の指針
- 06LLMのセキュリティ対策
活用シーン
- 01Web機能のセキュリティ対策の計画
- 02認証やファイルアップロード変更のレビュー
- 03外部入力を扱う連携の強化
- 04LLM機能のリスク評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ maddec01/ mux-factory --skill security-and-hardeningインストール後、Claude Code で/security-and-hardeningを入力して読み込まれたか確認します。
同梱ファイルreferences/hardening-patterns.md
詳細
セキュリティと堅牢化
概要
Web アプリケーション向けのセキュリティ優先の開発プラクティスです。外部からの入力はすべて悪意あるものとして扱い、すべてのシークレットを厳重に保護し、認可チェックは必須とします。セキュリティは特定の工程ではなく、ユーザーデータ、認証、外部システムに関わるすべてのコード行に課される制約です。
使用する場面
- ユーザー入力を受け付けるものを構築する場合
- 認証または認可を実装する場合
- 機密データを保存または送信する場合
- 外部 API やサービスと連携する場合
- ファイルのアップロード、Webhook、コールバックを追加する場合
- 決済データや PII を扱う場合
プロセス:まず脅威モデルを作成する
脅威モデルなしに後付けされる対策は、当て推量にすぎません。堅牢化に取りかかる前に、5 分間、攻撃者の視点で考えてください。
- 信頼境界を洗い出します。 信頼できないデータは、どこからシステムに入ってきますか? HTTP リクエスト、フォームフィールド、ファイルのアップロード、Webhook、サードパーティ API、メッセージキュー、LLM の出力に加え、OS から渡されるため内部の値のように見えるローカル値、つまり別プロセスのコマンドラインや環境変数、共有ボリューム上のファイル名、ジョブのペイロード内のパスも含まれます。値の信頼性は、どの経路で届いたかではなく、誰が書き込んだかで決まります。すべての境界が攻撃対象領域です。
- 資産を明確にします。 盗まれたり、破壊されたりすると困るものは何ですか? 認証情報、PII、決済データ、管理者操作、資金の移動などです。
- 各境界に STRIDE を適用します — 儀式ではなく、手早く検討するための視点です。
| 脅威 | 問いかけ | 典型的な対策 |
|---|---|---|
| Spoofing(なりすまし) | 誰かがユーザーやサービスになりすませますか? | 認証、署名の検証 |
| Tampering(改ざん) | 転送中または保存中のデータを改ざんできますか? | 整合性チェック、パラメーター化クエリ、HTTPS |
| Repudiation(否認) | 後から操作を否認できますか? | セキュリティイベントの監査ログ |
| Information disclosure(情報漏えい) | データが漏えいする可能性はありますか? | 暗号化、フィールドの許可リスト、汎用エラー |
| Denial of service(サービス拒否) | 過負荷にできますか? | レート制限、入力サイズの上限、タイムアウト |
| Elevation of privilege(権限昇格) | ユーザーが本来持つべきでない権限を得られますか? | 認可チェック、最小権限 |
- ユースケースの横に悪用ケースを書きます。 各機能について、「これをどう悪用できるか?」と問いかけ、その内容を最初のテストにします。
機能の信頼境界を特定できないなら、その機能を安全にする準備ができていません。これは OWASP A04: Insecure Design に該当します。ほとんどの侵害は、コードではなく設計から始まります。
3 段階の境界システム
必ず実施する(例外なし)
- システム境界(API ルート、フォームハンドラー)ですべての外部入力を検証する
- すべてのデータベースクエリをパラメーター化する — ユーザー入力を SQL に連結しない
- XSS を防ぐために出力をエンコードする(フレームワークの自動エスケープを使用し、無効化しない)
- すべての外部通信にHTTPS を使用する
- パスワードを bcrypt/scrypt/argon2 でハッシュ化する(平文で保存しない)
- セキュリティヘッダーを設定する(CSP、HSTS、X-Frame-Options、X-Content-Type-Options)
- セッションには httpOnly、secure、sameSite Cookie を使用する
- リリースのたびに、検出されたパッケージマネージャーのネイティブ監査機能を、コミット済みのロックファイルに対して実行する
最初に確認する(人間の承認が必要)
- 新しい認証フローの追加、または認証ロジックの変更
- 機密データ(PII、決済情報)の新しいカテゴリの保存
- 新しい外部サービス連携の追加
- CORS 設定の変更
- ファイルアップロードハンドラーの追加
- レート制限またはスロットリングの変更
- 高い権限またはロールの付与
決して行わない
- シークレットをバージョン管理にコミットしない(API キー、パスワード、トークン)
- 機密データをログに記録しない(パスワード、トークン、クレジットカード番号の全桁)
- クライアント側の検証をセキュリティ境界として信頼しない
- 利便性のためにセキュリティヘッダーを無効化しない
- ユーザー提供データに対して
eval()やinnerHTMLを使用しない - クライアントからアクセス可能なストレージにセッションを保存しない(認証トークンを localStorage に保存しない)
- スタックトレースや内部エラーの詳細をユーザーに公開しない
セキュリティ強化の制御
以下のルールがワークフローです。それぞれの具体的な実装はreferences/hardening-patterns.mdにあります。必要なコードに取り組む段階になってから、該当するセクションを開いてください。
インジェクション、XSS、アクセス制御
- すべてのクエリをパラメーター化してください。入力文字列から SQL、NoSQL、シェルコマンドを組み立ててはいけません。
- フレームワークの自動エスケープを使って出力をエンコードしてください。生の HTML が避けられない場合は、先に許可リスト方式のサニタイザーでサニタイズしてください。
- 認証だけでなく、すべてのリクエストで認可を確認してください。認証済みユーザーは、対象の特定のリソースを所有しているか、そのリソースへのアクセスを許可されている必要があります(A01、IDOR)。
認証とセッション
- パスワードは bcrypt(≥12 rounds)、scrypt、または argon2 でハッシュ化してください。セッションシークレットは環境変数から取得し、コードに記述してはいけません。
- セッション Cookie には
httpOnly、secure、sameSite: 'lax'または'strict'を設定してください(CSRF 対策です。'none'はクロスサイトリクエストでも Cookie を送信します)。また、maxAgeに上限を設定してください。
パターン:認証。
ヘッダー、CORS、レスポンス
- すべてのレスポンスにセキュリティヘッダーを設定してください(helmet またはフレームワークの同等機能)。CSP は
default-src 'self'から始め、制限を緩めず、さらに厳しくしてください。 - CORS は、設定から取得する明示的なオリジンリストに制限してください。認証情報を使う場合に
*を指定してはいけません。 - どのレスポンスからも機密フィールド(
passwordHash、リセットトークン)を取り除いてください。エラーボディーには汎用的な内容のみを含め、内部情報はサーバーログにのみ記録してください。
入力検証とアップロード
- 境界でスキーマを使って検証してください。許可リストに基づく形状、長さ、列挙値、形式を確認します。構造化された詳細情報を添えて 422 で拒否し、以降のコードでは解析済みの型付き値のみを使用してください。
- アップロードでは、MIME タイプを許可リストで制限し、サイズに上限を設け、必要に応じてコンテンツ(マジックバイト)を検証してください。拡張子は何の証明にもなりません。
パターン:スキーマ境界での検証、ファイルアップロード。
サーバー側のフェッチ(SSRF)
ユーザーが影響を与えられる URL(Webhook、URL からのインポート、画像プロキシ、リンクプレビューなど)は、内部サービスを標的にできる可能性があります。スキームとホストを許可リストで制限し、DNS レコードをすべて解決して、プライベートアドレスまたは予約済みアドレス(ループバック、リンクローカル 169.254.169.254、プライベート、ユニークローカル。IPv4 と IPv6 の両方)を拒否し、リダイレクトを禁止してください。このチェックには DNS リバインディングによる TOCTOU の隙が残ります。リスクの高い箇所では、解決した IP を固定するか、フィルタリングエージェントを前段に配置してください。
パターン:SSRF。
派生パスに対する破壊的操作
削除、移動、上書きの安全性は、対象を示す値の安全性に左右されます。また、その値への信頼は、どの経路から届いたかではなく、誰が書き込んだかに基づきます。別のプロセスのコマンドラインも、フォームフィールドと同様に攻撃者が制御できるものです。形式のチェックで証明できるのは、値が適切な形であることだけであり、認可されていることではありません。操作の前に、次の3つすべてを必須としてください。解決済みの対象(シンボリックリンクを解決済み)が許可リストにあるルート配下にあること、そのルートから少なくとも1階層下にあること、そして操作前に読み取った所有権の証拠があること。拒否した場合は、拒否した対象をログに記録して処理を停止してください。より広範なデフォルトパスにフォールバックしてはいけません。
このチェックが見かけほど強固でない理由(マーカーによる自己証明、チェックと使用の競合):破壊的操作の対象パス。実際のコード例:../../references/security-checklist.md。
レート制限
API 全般にはレート制限を設け、認証エンドポイントには厳格に適用します(15 分間に約 10 回の試行)。複数のプロセスがトラフィックを処理するようになると、メモリー内のカウンターでは上限が気付かないうちに「上限 × インスタンス数」になったり、サーバーレス環境では機能しなくなったりします。共有ストアを使ってレート制限を管理してください。
パターン:レート制限。
シークレット
シークレットは環境変数から取得します。プレースホルダーを含む .env.example はコミットし、実際の .env* ファイルと鍵情報は gitignore に登録します。コミット前に、ステージ済みの差分を grep で確認してください。シークレットがリモートに到達した時点で、そのシークレットは漏えいしたものとして扱います。まずローテーションし、その後で履歴から削除してください。
パターン:シークレットの管理。
依存関係とサプライチェーン
- インストールの境界とパッケージマネージャーを特定します。 ロックファイルを所有するワークスペースルートを使用します。そのワークスペースの外にある独立したネストプロジェクトの場合に限り、そのプロジェクトを使用します。
packageManager(存在する場合)、ロックファイル、CI の内容を照合し、食い違いや競合するロックファイルがあれば作業を中止します。パッケージマネージャーのバージョンを固定してください。 - 依存関係のスクリプトは、最初に実行される前にブロックします。 スクリプトを無効にするか、文書化された fail-closed ポリシーを適用してブートストラップし、実行待ちのスクリプトのソースを確認します。必要最小限のみ許可し、ポリシーをコミットしてから、クリーンな状態で frozen/immutable インストールを実行して確認してください。すべてを一括で許可してはいけません。
- リリースのたびに、コミット済みのロックファイルを対象にネイティブの監査を実行します。 critical/high の脆弱性は、到達可能性(ランタイム、ビルド、テスト、デプロイの各経路)に基づいて優先順位を付け、修正します。強制的な修正(
npm audit fix --forceなど)は自動適用しないでください。宣言された依存関係の範囲を超える可能性があるためです。適用前にプレビューし、変更履歴を読み、アップグレードごとにテストします。対応を保留する場合は、理由と見直し日を必ず記録してください。 - 監査で検出できるのは、既知のアドバイザリのみです。 新たに悪意あるパッケージやタイポスクワッティングされたパッケージ(
cross-envとcrossenvのようなもの)は検出できません。新しい依存関係、ロックファイルの差分、スクリプトポリシーの変更は、所有者、メンテナンス状況、リリースからの経過期間、来歴、推移的な依存関係グラフを含めてまとめて確認してください。対応している場合はレジストリ署名を検証し(npm audit signatures、pnpm audit signatures)、署名がないことは調査すべき兆候として扱います。ただし、それだけで侵害の証拠と決めつけないでください(A06、LLM03)。
トリアージの判断ツリー:依存関係監査のトリアージ。パッケージマネージャーのマトリックスとインストールスクリプトのゲートについては、../../references/security-checklist.md を参照してください。
個人データとプライバシー
セキュリティ強化では「攻撃者がそのデータを読めるか?」を問います。プライバシーでは「そもそも、私たちはそのデータを保持すべきか? どのくらいの期間か?」を問います。保護、漏えい対応、コンプライアンス対応のコストが最も低いデータは、収集しなかったデータです。個人データは最小限に抑えるべき負債として扱ってください。
- フィールドを追加する際に分類します(個人情報に該当しない情報、PII、機微情報)。各分類に応じて適切に取り扱ってください。所在を特定できないデータは保護できず、削除要求にも対応できません。
- 明示した目的に沿ってのみ収集します。 「あとで役に立つかもしれない」は目的ではなく、潜在的な漏えい範囲です。PII をテレメトリーに含めないでください(
observability-and-instrumentationスキルでも運用の観点から同じ点を説明しています)。 - 最初に保持期間を設定し、実際に削除します。 個人データを保存する場所すべてに TTL と機能する削除経路が必要です。バックアップ、キャッシュ、検索インデックス、分析用のコピーも対象に含めてください。
- 管轄区域で求められるデータ主体の権利(GDPR、CCPA など)をサポートします。エクスポート、訂正、削除に対応してください。ユーザーのデータをシステム全体から検索・消去でき、不可逆的に分散しないスキーマを設計してください。
- 同意は収集と第三者への共有を許可する条件であり、監査可能である必要があります。 分析、広告、LLM のベンダーに PII を送ることは共有にあたります。ベンダーとのデータ処理契約が必要です。地域はハードコードされた前提ではなく、設定可能なポリシーにしてください。
分類表:データ分類。プライバシーインシデントが発生したら、データ漏えいの通知期限の計測が始まります。debugging-and-error-recovery スキルを使って事後検証を実施してください。
AI/LLM 機能
LLM の呼び出し(チャットボット、要約ツール、エージェント、RAG)によって新たな攻撃対象領域が生まれます。OWASP Top 10 for LLM Applications (2025) に照らして確認してください。
- モデルの出力は信頼できない入力です(LLM05)。
eval、SQL、シェル、innerHTML、ファイルパスには決して渡さないでください。防御的に解析し、スキーマに照らして検証してから、エンコードしてください。 - プロンプトは乗っ取られる可能性があります(LLM01)。コンテキスト内の信頼できないテキスト(ユーザーメッセージ、取得したページ、PDF など)には、指示が含まれている場合があります。システムプロンプトはセキュリティ境界ではありません。権限はコードで強制してください。
- 秘密情報、他のテナントのデータ、システムプロンプト全体をコンテキストウィンドウに含めないでください(LLM02、LLM07)。ツールの権限範囲を限定し、すべてのツール引数を検証し、破壊的な操作を確認してください(LLM06)。トークン数、リクエストレート、再帰の深さに上限を設けてください(LLM10)。RAG の埋め込みをテナントごとに分離し、インデックス登録の前にドキュメントを検証してください(LLM08)。
パターン:LLM 出力の処理。
レビューチェックリスト
承認前に、../../references/security-checklist.md を確認してください。認証、認可、入力、データ保護とプライバシー、ヘッダーと CORS、依存関係とサプライチェーン、AI/LLM、エラー処理に加え、OWASP のクイックリファレンステーブルが網羅されています。
よくある合理化
| 合理化 | 実態 |
|---|---|
| 「これは内部ツールなので、セキュリティは重要ではない」 | 内部ツールも侵害されます。攻撃者は最も弱い箇所を狙います。 |
| 「セキュリティは後から追加すればいい」 | セキュリティを後付けするのは、最初から組み込むより10倍難しくなります。今すぐ追加してください。 |
| 「これを悪用しようとする人はいない」 | 自動スキャナーが見つけます。隠すことによるセキュリティは、セキュリティではありません。 |
| 「フレームワークがセキュリティを処理してくれる」 | フレームワークが提供するのはツールであり、保証ではありません。正しく使う必要があります。 |
| 「これは単なるプロトタイプだ」 | プロトタイプは本番環境になります。初日からセキュリティの習慣を身につけてください。 |
| 「ここで脅威モデリングをするのは大げさだ」 | 「どうすればこれを攻撃できるか?」を5分考えるだけで、後からどんな対策をしても修正できない設計上の欠陥を防げます。 |
| 「LLM の出力にすぎず、ただのテキストだ」 | その「テキスト」は SQL 文、スクリプトタグ、シェルコマンドの可能性があります。他の信頼できない入力と同じように扱ってください。 |
| 「監査に合格したので、この依存関係は安全だ」 | 監査で確認できるのは既知のアドバイザリです。新たに悪意を持って作られたパッケージを検出したり、未レビューのインストールスクリプトを安全に実行できるようにしたりするものではありません。 |
| 「今収集しておけば、後で必要になるかもしれない」 | 保有していないデータは、漏えい、召喚状による開示、誤削除の対象になりません。「必要になるかもしれない」は目的ではなく、漏えい時の影響範囲です。 |
| 「削除リクエストは手作業で対応すればいい」 | 手作業による消去では、バックアップ、キャッシュ、分析用コピーを見落とします。スキーマからユーザーのデータを見つけられなければ、リクエストに対応できません。対応できるように設計してください。 |
| 「コンプライアンスは法務の仕事で、私たちの仕事ではない」 | エクスポート、削除、保持、同意はスキーマとコードに関わります。PII を10個のシステムにばらまいた後で、法務が後付けすることはできません。 |
危険な兆候
- ユーザー入力がデータベースクエリ、シェルコマンド、または HTML のレンダリングに直接渡されている
- 削除、移動、上書きの対象がペイロード、設定値、または別のプロセスのコマンドラインから取得され、パスの形式チェックだけで保護されている
- ソースコードまたはコミット履歴にシークレットが含まれている
- 認証または認可チェックのない API エンドポイント
- CORS 設定がない、またはワイルドカード(
*)オリジンが設定されている - 認証エンドポイントにレート制限がない、または複数のインスタンスの前段にインメモリのリミッターがある
- スタックトレースや内部エラーがユーザーに公開されている
- 既知の重大な脆弱性がある依存関係、同じインストール境界に競合するロックファイルがある、再現可能でないインストール、またはスクリプトが一律に承認されている
- サーバーが許可リストなしでユーザー指定の URL を取得している(SSRF)
- LLM/モデルの出力がクエリ、DOM、シェル、または
evalに渡されている - シークレット、PII、またはシステムプロンプト全体が LLM のコンテキストウィンドウに含まれている
- 明示された目的、保持期間の上限、または削除手段なしに個人データが収集されている
- 同意またはデータ処理契約なしに PII が分析/広告/LLM ベンダーに送信されている
- 「アカウントを削除する」がフラグを切り替えるだけで、個人データがストアやバックアップに残り続けている
検証
セキュリティに関わるコードを実装した後に、次を確認してください。
- ネイティブ監査で、緩和されていない到達可能な重大/高リスクの検出事項がない。CI では正式なロックファイルが保持され、未レビューの依存関係スクリプトがブロックされている
- ソースコードまたは git 履歴にシークレットがない
- すべてのユーザー入力がシステム境界で検証されている
- ファイルシステムに対する破壊的操作の前にシンボリックリンクを解決し、許可リストにあるルート、最小の深さ、所有権を確認している
- 保護対象のすべてのエンドポイントで認証と認可を確認している
- レスポンスにセキュリティヘッダーが含まれている(ブラウザーの DevTools で確認)
- エラーレスポンスに内部の詳細が含まれていない
- 認証エンドポイントでレート制限が有効になっており、複数のインスタンスがトラフィックを処理する場合は共有ストアを使用している
- サーバー側で取得する URL が許可リストに対して検証されている(SSRF がない)
- LLM/モデルの出力が、使用前に検証およびエンコードされている(AI 機能がある場合)
- 個人データが分類され、明示された目的に必要な最小限に抑えられ、保持期間の上限が設定されている
- 削除およびエクスポートのリクエストがエンドツーエンドで機能している(バックアップ、キャッシュ、分析用コピーを含む)