信頼性が高くセキュリティで保護された C++ プログラムを構築する

米国政府の出版物「NISTIR 8397: Guidelines on Minimum Standards for Developer Verification of Software」には、任意のプログラミング言語で信頼できる安全なソフトウェアを構築する方法に関する優れたガイダンスが掲載されています。

このドキュメントは、NISTIR 8397 の構造に従っています。 各セクションの内容は以下のとおりです。

  • C++ やその他の言語用の Microsoft 開発者向け製品を使用して、そのセクションのセキュリティ ニーズを満たす方法を要約します。
  • 各領域で最大限の価値を得るためのガイダンスを提供します。

2.1 脅威モデリング

まとめ

脅威モデリングは重要なプロセスで、特に開発ニーズに合わせてスケーリングされ、ノイズを軽減するように適用される場合に役に立ちます。

Recommendations (推奨事項)

脅威モデリングは、動的なセキュリティ開発ライフサイクル (SDL) の一部である必要があります。 製品全体、特定の機能、主要な設計、または実装の変更に関する推奨事項を次に示します。

  • 堅牢で動的な SDL を使用して、開発者チームとの早期エンゲージメントおよびアプローチの適切なサイジングを可能にします。
  • 適切な方法で脅威モデリングを適用します。 すべての機能に脅威モデリングを適用しますが、戦術的には、公開されている複雑な機能または重要な機能から開始します。 トップダウン製品レビューの一環として、代わりにそれを定期的に適用します。
  • (すべてのセキュリティ要件と同様) 設計を変更する機会がまだ残っている早い段階で脅威モデリングを適用します。 また、脅威モデルは、攻撃面の減少やセキュリティ設計など、他のプロセスへの入力としても機能します。 後で作成される脅威モデルは、侵入テスト、またはファジングなどのセキュリティ テストが必要な領域のための "調査" に過ぎません。 ベースライン脅威モデルを作成したら、それを、攻撃面の変化に合わせて繰り返し実行することを計画します。
  • 資産インベントリとコンプライアンスを使用して、製品の構成要素を適切に追跡します。また、セキュリティ アーティファクト (脅威モデルを含む) とそれが適用される資産も追跡します。 このアプローチにより、自動リスク評価が向上し、変化する特定のコンポーネントや機能に対するセキュリティ対策に集中できるようになります。
  • Azure では、2022 年、Microsoft Threat Modeling Tool が Azure 開発用に更新されました。 詳細については、Microsoft Threat Modeling Tool の概要 - Azure に関するページを参照してください

サポート要素とプラクティス

脅威モデリングを適切に適用し、過少使用/過剰使用を回避するには、最初に次の主要な概念に取り組む必要があることがわかりました。

開発アプローチ

まず、チームの開発アプローチについて理解します。 毎日数十もの変更をアジャイル開発ワークフローを使用して運用環境にプッシュしているチームにとって、機能変更のたびに脅威モデルの更新を要求するのは現実的ではなく、また合理的でもありません。 代わりに、最初から、機能の要件を記述するときに、セキュリティ要件に関するアンケートを含めておくことを検討してください。 このアンケートでは、SDL の将来的な側面を判断するために、機能について具体的な質問をすることに焦点を当てる必要があります。 次に例を示します。

  • この機能によって、マルチテナント環境で顧客の分離を提供する方法の設計は大きく変わりますか? もしそうなら、完全な脅威モデルを実行することを検討してください。
  • 新しい機能ではファイルのアップロードが許可されますか? もしそうなら、Web アプリケーションのセキュリティ評価の方が適しているかもしれません。
  • この変更で変わるのは主に機能 UI だけですか? もしそうなら、従来の自動ツール以外に必要なものはありません。

このようなセキュリティに関するアンケートの結果により、どの SDL 手法をどの開発ユニットに結び付けるかが決まります。 また、その機能の SDL タイムラインが開発パートナーに通知されるため、適切なタイミングで共同作業を行えるようになります。

製品の在庫

次に、評価を任されている製品の資産インベントリをしっかりと管理します。 製品は複雑になっていきます。 接続されているデバイス用ソフトウェアを作成するのが一般的です。

  • センサー (旅客鉄道、車両など)
  • 車両内の他のコンポーネントと通信するバスベースのネットワーク (CANBUS、PROFIBUS など)
  • 顧客のデバイスやクラウド バック エンドとの通信のためのワイヤレス/携帯ネットワーク/Bluetooth
  • デバイスまたはフリート管理アプリケーションにフィードバックされるクラウド内の機械学習
  • その他にも用途はあります。

このような複雑な製品では、脅威モデリングが重要です。 強力な資産インベントリを使用すると、製品スタック全体を表示して全体像を把握したり、新機能や変更された機能が製品のセキュリティに与える影響を評価する必要がある重要な場所を確認したりできます。

細分性と統合

明確なメトリックを使用して、コンプライアンスを測定するシステムを確立します。

  • 機能レベルの開発のコンプライアンスを定期的に測定します。 機能のコンプライアンスは通常、開発者のシステム上やコードのコミット/マージ時であっても、より高い頻度、より細かな細分性で測定する必要があります。
  • 機能またはコンポーネントが使用されている、より広範な製品に対するセキュリティを定期的に評価します。 広範な評価は通常、モジュール上またはシステムのテスト時などに、より低い頻度、より粗い細分性で行われます。

スケール

セキュリティ アーティファクトと脅威モデル レビューの出力をキャプチャして保存する適切な資産インベントリ システムを保持します。 明確なインベントリを使用すると、パターンのレビュー出力を評価し、製品セキュリティ プログラムを定期的に調整する方法について、合理的な意思決定を行うことができます。

要件フェーズのセキュリティ アンケート、脅威モデリングの結果、セキュリティ評価の結果、自動化されたツールからの結果を組み合わせてみてください。 これらを組み合わせることで、特定の製品の相対的リスクの観点を自動化し、脅威モデリングから最大の価値を引き出すために重視すべきことを、理想的には "ダッシュボード" として、セキュリティ チームに通知することができます。

2.2 自動テスト

まとめ

自動テストは、コードの品質と安全性を確保するための重要な方法です。 これは、脅威モデリングなど、このドキュメントで説明されている他の領域をサポートする際に不可欠です。 他の安全なコーディングプラクティスと組み合わせて使用すると、コードベースにバグや脆弱性が持ち込まれるのを防ぐのに役立ちます。

キーの属性

テストは、信頼性が高く、一貫性があり、分離されていなければなりません。 これらのテストでは、できるだけ多くのコードをカバーする必要があります。 コードの長期的なセキュリティと信頼性を確保するために、すべての新機能とバグ修正に対応するテストが必要です (可能な場合)。 できるだけ多くの環境で自動テストを定期的に実行し、テストが確実に実行されること、およびすべての領域がカバーされていることを確認します。

  • 最初に実行する必要がある場所は、変更を加える先のマシンです。 テストは、編集に使用されている IDE 内で非常に簡単に実行できます。または開発者が変更を行う際、コマンド ライン上でスクリプトとして実行することもできます。
  • 次は、pull request のコミット/マージ プロセスの一部として実行する必要があります。
  • 最後に、継続的インテグレーションと継続的デプロイ (CI/CD) パイプラインの一部として、またはリリース候補のビルド上で実行します。

テストの範囲は各ステップで広げ、最後のステップでは、他のステップでカバーできなかった可能性があるすべての領域を網羅するようにします。

継続的な使用とメンテナンス

テストの信頼性は、テスト スイートの有効性を維持するうえで重要です。 テストの失敗を割り当てて調査する必要があります。潜在的なセキュリティの問題が優先され、迅速かつ事前に決められた時間内に更新されます。 テストの失敗を当たり前のように無視するべきではありません。無視するには強い正当性と承認が必要です。 テスト スイート自体の問題によるテストの失敗も、他のエラーと同じように扱い、カバレッジ不足によって製品の問題が見逃されるのを防ぐ必要があります。

テストの種類 (特に単体テスト)

自動テストには種類がいくつかあります。必ずしもすべてのテストをすべてのアプリケーションに適用できるわけではありませんが、優れたテスト スイートにはさまざまな種類のテストが含まれています。 単体テストなどのコード ベースのテスト ケースは最も一般的かつ不可欠であり、すべてのアプリケーションに適用され、できるだけ多くのコード パスを意図的にカバーし、正確さを追求しています。 これらのテストは小さくして、迅速に実行できるように、またマシンの状態に影響を与えないようにします。そうすれば、完全なテスト スイートを迅速かつ頻繁に実行できます。 可能な場合は、ハードウェアのセットアップが異なる多くのマシンでテストを実行し、1 種類のマシンでは再現できない問題を発見できるようにします。

Visual Studio

Visual Studio テスト エクスプローラーでは、最も一般的な C++ テスト フレームワークの多くがネイティブにサポートされています。また、他のフレームワーク向け拡張機能をインストールするオプションもあります。 この柔軟性は、作業中のコードをカバーするテストのサブセットを実行する際に役に立ち、テスト エラーが発生しても簡単にデバッグできます。 また、Visual Studio では、既存のプロジェクト用に新しいテスト スイートを設定するのも簡単で、CodeLens などの便利なツールを使えば、これらのテストを容易に管理できます。 Visual Studio を使用した C/C++ テストの作成、実行、管理の詳細については、C/C++ 用の単体テストの作成 - Visual Studio (Windows) に関するページを参照してください。

Azure と GitHub CI/CD の場合

より詳細な検証を行い、実行に時間がかかるテスト (静的分析、コンポーネント検出など) は、pull request テストや継続的インテグレーション テストに適しています。 Azure DevOps と GitHub Actions を使用すると、検証の自動実行や、検証が失敗した場合のコード チェックインのブロックを容易に行えます。 適用の自動化により、チェックインされるすべてのコードの安全性が、より厳格なチェックが実行されることに基づいて確保されます。 ここでは、Azure Pipelines と Azure DevOps ビルド検証について説明します。

2.3 コードベース (静的) 分析

概要: 静的コード/バイナリ分析は既定で有効にして、既定で安全性を確保する必要があります。 静的分析では、必要な安全ポリシーとセキュリティ ポリシーに関するプログラムが、ビルド時に分析されます。顧客のマシンで悪用が発生する可能性がある実行時ではありません。 静的分析では、プログラムを、ソース コード形式またはコンパイル済みの実行可能ファイル形式で分析できます。

推奨事項: Microsoft では、次のことをお勧めします。

  • 入力ソース コード (コンパイル前) と実行可能バイナリ (コンパイル後) の両方について、すべての C++ プログラムの静的分析を有効にします。 "有効にする" とは、分析を、開発者のマシン上でビルドのたびに、または後でコードを検査するために別のビルドとして、またはチェックイン要件として実行することを意味する場合があります。
  • 静的分析をテストの一形態として CI パイプラインに組み込みます。
  • 定義上、静的分析には誤検知が伴います。その事実を品質フィードバック ループに組み込む準備をします。 誤検知の少ないすべての警告をすばやく事前に有効にします。 その後、重要なバグにフラグを設定するルールを定期的に追加し、誤検知を少しずつ増やしながら、コードベースが warning-clean でコンパイルされるルールの数を徐々に増やしていくように積極的に取り組みます (最初は、それらのルールのコード ベースがクリーニングされる前も)。
  • サポートされている最新バージョンの Visual Studio を常に使用し、使用可能になった最新のパッチ リリースを、次の開発ステージ/サイクルに遅れることなく、すぐに使用できるようにエンジニアリング環境を設定します。

主要なツール: 以下を意識して使用します。

注:

  • /analyze を使用すると、コンパイル時に C++ コードに対して静的分析を実行し、重大なセキュリティと信頼性のコードの脆弱性を特定できます。 これは、C++ プログラムの開発タイムライン全体で有効にする必要があります。 最初に、最小ベースラインとして既定で少なくとも "Microsoft Native 推奨" を有効にします。 その後、エンジニアリング ポリシーの要求に従って、さらにルールを指定する方法、特に C++ Core Guidelines のルールを指定する方法をドキュメントで確認します。 ソース コードの静的分析機能は、Visual C++ IDE とコマンド ラインのビルド ツールの両方で使用できます。
  • /W4/WX を可能な限り有効にして、高い警告レベルでコードがクリーンにコンパイルされるように (W4)、また、修正が必要なエラーとして警告が扱われるようにします (WX)。 これらのオプションを使用すると、他の静的分析ツールで確認できない未初期化データ エラーを見つけ出すことができます。このエラーは、コンパイラ バックエンドが、相互分析とインライン化を実行した後にのみ表示されるためです。
  • BinSkim バイナリ分析により、プロジェクトのさまざまなセキュリティ機能が有効になります。 BinSkim により PDB やその他の出力が生成され、証拠保全の一貫性の検証やセキュリティの問題への効率的な対応が容易になります。 Microsoft では、BinSkim ツールを実行して、プログラムで生成または使用されるすべての実行可能バイナリ (.sys.dll、または .exe) を分析することをお勧めします。 BinSkim ユーザー ガイドには、サポートされているセキュリティ標準の一覧が記載されています。 Microsoft では、BinSkim ツールによって "エラー" として報告されたすべての問題を修正することをお勧めします。 "警告" として報告された問題については、その問題を解決するとパフォーマンスに影響を与えたり、不要な修正だったりする可能性があるため、選択的に評価する必要があります。

Azure と GitHub CI/CD の場合: Microsoft では、リリース CI/CD シナリオでは、ソース コードとバイナリ静的分析を常に有効にしておくことをお勧めします。 ソースのバグをできるだけ早く発見し、全体的なコストを最小限に抑えるため、ソース分析は、ローカル開発者のマシンで直ちに実行するか、少なくともコミット要求または pull request ごとに実行します。 バイナリ レベルのバグはゆっくりと発生する傾向があるため、バイナリ分析は、頻度が低いリリース前 CI/CD シナリオ (夜間ビルドや週 1 回のビルドなど) で実行すれば十分かもしれません。

2.4 ハードコーディングされたシークレットのレビュー

まとめ

ソフトウェア内ではシークレットをハードコーディングしないでください。 ソース コードからシークレットを効率的に見つけて削除するには、ソース コード ベース全体をスキャンできる信頼性の高いツールを使用します。 シークレットを見つけたら、安全なストレージとシークレットの使用に関するガイドラインに従って、安全な場所に移動します。

問題

"シークレット" とは、ID を確立してリソースへのアクセスを提供するエンティティ、または機密データの署名や暗号化に使用されるエンティティのことです。 たとえば、パスワード、ストレージ キー、接続文字列、秘密キーなどがあります。 ソフトウェア製品にシークレットを保存して、ソフトウェアで必要になったらすぐに取得できるようにしたくなります。 しかし、このようなハードコーディングされたシークレットは、発見されやすく、サービスやデータを侵害するのに使用できるため、重大または致命的なセキュリティ インシデントを引き起こす可能性があります。

防止

ソース コードで (プレーン テキストまたは暗号化された BLOB として) ハードコーディングされたシークレットはセキュリティの脆弱性です。 ソース コードにおけるシークレットを回避する方法に関する一般的なガイドラインを次に示します。

  • ソース管理に送信する前に、事前チェックイン ツールを使用して、コード内の潜在的なハードコーディングされたシークレットをスキャンし、発見します。
  • ソース コードまたは構成ファイルにクリア テキストの資格情報を配置しないでください。
  • SharePoint、OneNote、ファイル共有などにクリア テキストの資格情報を保存しないでください。 また、メール、IM などで共有しないでください。
  • 簡単に検出できる解読キーを使用してシークレットを暗号化しないでください。 たとえば、パスワードを含むファイルと一緒に PFX ファイルを保存しないでください。
  • 脆弱な解読を使ってシークレットを暗号化しないでください。 たとえば、脆弱なパスワードまたは一般的なパスワードを使用して PFX ファイルを暗号化しないでください。
  • 暗号化された資格情報をソース コードに配置することは避けてください。 代わりに、ソースではプレースホルダーを使用し、デプロイ システムで、そのプレースホルダーが、承認されたストアのシークレットに置き換えられるようにします。
  • 運用環境のデプロイと同じ原則を、テスト、ステージングなどの環境のシークレットに適用します。 敵対者は、多くの場合、管理が不十分な非運用システムをターゲットにし、それを使って狙いを運用環境に切り替えます。
  • テスト、ステージング、運用などのデプロイ間でシークレットを共有しないでください。

ハードコーディングされたシークレットとは直接関係ありませんが、テスト、開発、運用のシークレットを保護することも忘れないでください。

  • シークレットのローテーションは定期的に、また公開された可能性がある場合は必ず行います。 シークレットをローテーション/再デプロイできることが実証されていることは、安全なシステムの証拠になります。 特に、この機能が存在しないことは、不可避の脆弱性を示す強力な証拠です。
  • "私のテスト資格情報はリスクを生まない" という、よくある開発者の理屈をそのまま受け止めないでください。実際は、ほぼ常にリスクを生みます。
  • RBAC/ID ドリブン ソリューションを、シークレットの誤った管理を回避できる優れたエンジニアリング ソリューションとして優先し、シークレット (パスワード、ベアラー キーなど) から完全に移行することを検討します。

検出

製品のレガシ コンポーネントのソース コードに、ハードコーディングされたシークレットが隠れている場合があります。 開発者のデスクトップ マシンのシークレットがリモート ブランチに入り込み、リリース ブランチにマージされ、意図せずに漏えいすることがあります。 ソース コードに隠れている可能性があるシークレットを検出するには、コードをスキャンしてハードコーディングされたシークレットを探すことができるツールを使用できます。

修正

ソース コードから資格情報が見つかった場合は、すぐに公開されているキーを無効にし、公開に基づいてリスク分析を実行する必要があります。 システムを稼働させておく必要がある場合でも、次の手順で、シークレット マネージャーを有効にして修復することができます。

  1. 修復でマネージド ID への切り替えが許可されている場合、または Azure Key Vault (AKV) などのシークレット マネージャーでの削除が必要な場合は、最初にそれを行います。 その後、更新された ID またはキーを使用して再デプロイします。
  2. 公開されたシークレットを無効にします。
  3. 侵害による破損の可能性の監査/リスク評価を実行します。

クラウド アプリやサービスで使用される暗号化キーやその他のシークレットを保護するには、適切なアクセス ポリシーで Azure Key Vault を使用します。

公開によって特定の顧客データ/PII が侵害された場合は、他のコンプライアンス/レポート要件が必要になる可能性があります。

無効になったシークレットをソース コードから削除し、それを別の方法に置き換えて、シークレットがソース コードに直接公開されないようにします。 Azure AD などのツールを使用して、可能な限りシークレットを排除する機会を探します。 Azure Active Directory を介してマネージド ID を利用するように認証方法を更新できます。 シークレットの保存と管理には、Azure Key Vault (AKV) などの承認されたストアのみを使用します。 詳細については、以下を参照してください:

Azure DevOps (AzDO)

AzDO ユーザーは、GitHub Advanced Security for Azure DevOps (GHAzDO) を使用してコードをスキャンできます。 GHAzDO を使用すると、ユーザーはリポジトリでプッシュ保護を有効にして、漏えい前に潜在的な公開を発見し、シークレットの公開を防ぐことができます。 Azure DevOps のコードでハードコーディングされたシークレットを検出する方法の詳細については、次の各リンクで GitHub Advanced Security for Azure DevOps のシークレット スキャンに関するトピックを参照してください。

GitHub の場合

シークレット スキャンは、GitHub.com で次の 2 つの形式で使用できます。

  • パートナー向けシークレット スキャン アラート。 すべてのパブリック リポジトリで自動的に実行されます。 シークレット スキャン パートナーによって指定されたパターンと一致するすべての文字列が、関連するパートナーに直接報告されます。
  • ユーザー向けシークレット スキャン アラート。 GitHub Enterprise Cloud を使用し、GitHub Advanced Security のライセンスを持つ組織が所有するリポジトリに対して、追加のスキャンを有効にして、構成することができます。 これらのツールは、プライベート リポジトリと内部リポジトリもサポートしています。

GitHub には、パートナーとユーザーが使用できる既知のシークレット パターンが用意されています。このパターンは、ニーズに合わせて構成することができます。 詳細については、以下を参照してください。

Note

GitHub Advanced Security for Azure DevOps には、GitHub ユーザーが既に使用できるシークレット スキャン、依存関係スキャン、CodeQL コード スキャン ソリューションと同じものが用意されており、Azure Repos と Pipelines を保護するために Azure DevOps にネイティブに統合されます。

その他のリソース

2.5 言語と OS が提供するチェックと保護を使用して実行する

まとめ

バイナリのセキュリティ強化は、コンパイル時にセキュリティ制御を適用することで実現します。 これには、次のような軽減策が含まれます。

  • コード内の悪用可能な脆弱性を防ぐ。
  • 悪用に対するセキュリティ防御をトリガーするランタイム検出を有効にする。
  • データの生成とアーカイブを有効にして、セキュリティ インシデントによる損害を抑える。

バイナリ コンシューマーが強化のメリットを最大限に利用するには、Windows のセキュリティ機能を選択する必要があります。

Microsoft には、開発者がより安全なコードを記述して出荷できるように、C++ プロジェクトに固有の機能セットが用意されています。 C++ 開発者は、実行可能コードを生成する言語に共通するセキュリティ標準にも準拠する必要があります。 Microsoft では、このセクションで説明する多くの保護の使用を強制するのに役立つパブリック OSS バイナリ チェッカーである BinSkim を保持しています。 BinSkim の詳細については、Binskim ユーザー ガイド | GitHub を参照してください

バイナリ レベルのコントロールは、エンジニアリング プロセスのどこに適用されるかによって異なります。 コンパイラとリンカーのオプションは、厳密にコンパイル時に使用するもの、実行時のオーバーヘッドを伴うコード生成を変更するもの、OS 保護との互換性を実現するためにコード生成を変更するものに区別する必要があります。

開発者の設定では、できるだけ多くの静的分析を有効にしたり、デバッグを高速化するためにプライベート データの生成を有効にしたりすることをお勧めします。 リリース ビルドは、セキュリティ、パフォーマンス、およびその他のコード生成に関する問題の適切な組み合わせに合わせて調整する必要があります。 リリース プロセスは、パブリックなビルド データとプライベートで使用されるビルド データ (パブリック シンボルとプライベート シンボルなど) の生成と管理が適切に行われるように構成しなければなりません。

最新の状態を維持: 常に最新のコンパイラとツールを使用する

すべてのコードを現在のツールセットでコンパイルし、最新の言語サポート、静的分析、コード生成、セキュリティ制御を活用します。 コンパイラは生成されたすべてのコンポーネントに影響を与えるため、ツールの更新時に回帰が発生する可能性が比較的高くなります。 古いコンパイラを使用すると、チームがコンパイラをアップグレードする時間を十分にとれない可能性があるため、セキュリティ インシデントに対応するとき、是正措置に特定のリスクが生じます。 Microsoft では、コンパイラの更新プログラムを定期的に更新してテストする機能をチームが開発することを推奨しています。

安全な開発方法、言語バージョン、フレームワーク/API を使用する

次のように、コードでは、C++ の安全性とシンプルさを促進することでリスクを最小限に抑える、開発手法、言語バージョン、フレームワーク、API などを利用する必要があります。

  • 安全で一貫性のある最新の C++ コードを記述して、ベスト プラクティスに準拠し、一般的な落とし穴を回避するためのガイダンスについては、C++ Core Guidelines のガイドライン サポート ライブラリ (GSL) を参照してください。
  • C++ Core Guidelines で使用することが推奨されている関数と型については、Microsoft GSL 実装に関するページを参照してください。
  • リソース セーフな C++ コンテナー、C ランタイム ライブラリ (CRT) のメモリ オーバーフロー保護: リソース セーフな std::vectorstd::string を優先します。 C データを使用する必要がある場合は、バッファーの誤用や未定義の言語動作によるメモリの破損を防ぐために設計されている、安全な CRT 関数バージョンを使用します。
  • SafeInt ライブラリ は、算術演算と比較演算における整数のオーバーフローを防止します。

安全な依存関係を使用する

バイナリは、安全でないライブラリと依存関係にリンクしないでください。 開発チームは、外部のすべての依存関係を追跡し、これらのコンポーネントに CVE/特定されたセキュリティ脆弱性がある場合は、より安全なバージョンに更新して解決する必要があります。

コード来歴保証とセキュリティ対応の効率性を最大化する

コンパイルでは、バックドアやその他の悪意のあるコードの侵入の検出と防止に役立つ、強力なコード来歴保証を有効にする必要があります。 結果として得られるデータは、デバッグと調査にも不可欠であり、すべてのソフトウェア リリース用にアーカイブする必要があります。これにより漏えいした場合の効率的なセキュリティ対応が推進されます。 次のコンパイラ スイッチにより、セキュリティ対応に不可欠な情報が生成されます。

  • Visual C++ の /ZH:SHA_SHA256 - すべての PDB ソース ファイル ハッシュの生成に、暗号として安全なアルゴリズムが確実に使用されます。
  • Visual C++ の/Zi/ZI (デバッグ情報の形式) - クラッシュ データの収集やその他の一般使用シナリオ用に削除されたシンボルを公開するだけでなく、リリースされたすべてのバイナリに対して、プライベート PDB がビルドによって確実に生成およびアーカイブされるようにします。 バイナリ分析ツールには、コンパイル時に多くのセキュリティ軽減策が有効になっているかどうかを確認するために、完全なシンボルが必要です。 プライベート シンボルはセキュリティ対応では非常に重要であり、悪用が発生したときにエンジニアは損害を評価し制限するために競い合い、デバッグと調査のコストが削減されます。
  • Visual C++ リンカーの /SOURCELINK - PDB に Sourcelink ファイルを含める: ソース リンクは、バイナリのソース デバッグを提供する言語およびソース管理に依存しないシステムです。 ソース デバッグにより、リリース前のセキュリティ検証とリリース後のインシデント応答の範囲の効率が大幅に向上します。

コンパイラ エラーを有効にして、コード作成時の問題を回避する

コンパイルでは、セキュリティ関連のコンパイラ チェックを重大なエラーとして有効にする必要があります。次に例を示します。

バイナリを OS ランタイムのセキュリティ軽減策と互換性有りとしてマークする

コンパイラとリンカーの設定により、次のような悪意のあるコード実行を検出して軽減するコード生成機能をオプトインする必要があります。

機密情報漏えいを防止する

コンパイラ設定により、機密情報の検出防止をオプトインする必要があります。 近年、予測実行などのハードウェア機能に起因する意図しない情報漏えいがあることが、研究者によって発見されました。

ソフトウェア レベルで、予期せず漏えいしたデータが攻撃者に送信される可能性があります。 バッファーをゼロ初期化しなかったり、バッファーを誤用したりすると、信頼できる API を呼び出した攻撃者にプライベート機密データが漏えいする可能性があります。 このクラスの問題には、前述のように、追加の静的分析を有効にし、安全なリソース コンテナーを使用して対処することをお勧めします。

  • /Qspectre - 予測実行のサイドチャネル攻撃を軽減する - 予測実行によって生成される機密データの漏えいを防ぐバリア命令を挿入します。 これらの軽減策は、機密データをメモリに保存するための、信頼境界を越えて動作するコードに対して有効にする必要があります。 Spectre 軽減策を有効にする場合、Microsoft では常に、適切なベンチマークに対するパフォーマンスへの影響を測定することをお勧めします。これはパフォーマンスが重要なブロックまたはループに、ランタイム チェックが導入される可能性があるためです。 これらのコード パスでは、spectre(nomitigation) declspec 修飾子を使って軽減策をを無効にできます。 /Qspectre を有効にするプロジェクトは、Microsoft ランタイム ライブラリなど、これらの軽減策を使用してコンパイルされたライブラリにもリンクする必要があります。

2.6 ブラック ボックス テスト ケース

まとめ

ブラック ボックス テストは、テスト対象コンポーネントの内部動作を知っているかどうかに依存しません。 ブラック ボックス テストは、製品の機能のエンド ツー エンド機能を、任意のレイヤーまたはレベルでテストするように設計されています。 ブラック ボックス テストには、機能テスト、UI テスト、パフォーマンス テスト、統合テストがあります。 ブラック ボックス テストは、一般的な信頼性と機能の正確性を測定し、製品が期待どおりに動作することを確認する際に役に立ちます。

他のセクションとの関係

この種の要件ベースのテストは、脅威モデルで行われた仮定を検証し、そのセクションで取り上げた潜在的な脅威を対象とするのに役立ちます。 これらのテストは、製品の個別のコンポーネント間の統合、特に脅威モデルで説明されている信頼境界を越えた統合をテストするのに役立ちます。 ブラック ボックス テスト ケースは、ユーザー入力検証のためにすべての種類のエッジ ケースをテストする場合にも役立ちます。 既知のエッジ ケースとエラー ケースのテストはどちらも役に立ちます。 あまりはっきりしないケースをテストするファジングも役に立ちます。

オートメーションと回帰

これらのテストを定期的に実行し、その結果を以前の実行と比較することで、重大な変更やパフォーマンスの低下を見つけます。 また、これらのテストをさまざまなマシンやインストール セットアップで実行すると、さまざまなアーキテクチャや設定の変更によって発生する可能性のある問題に対処することができます。

クラッシュ ダンプ

これらのテストは、信頼性に関する問題を見つけるのに役立ち、クラッシュ、ハング、デッドロックなどが発生する可能性のあるさまざまなシナリオをテストできます。 テスト エラーの一部としてクラッシュ ダンプを収集し、そのダンプを Visual Studio に直接インポートして、コードのどの部分でこれらの問題が発生しているかをさらに調査することができます。 Visual Studio 内から機能テストを実行すると、ブラック ボックス内のどこでテストが失敗したかを正確に把握し、エラーを簡単にレプリケートしてデバッグし、修正をすばやくテストすることができます。

テストのデバッグを開始するには、テスト エクスプローラーを使用した単体テストのデバッグ - Visual Studio (Windows) に関するページを参照してください

Azure の場合

Azure DevOps も、Test Plans を使って、これらのテストを管理、検証することができます。 これらのテストを使用すると、手動検証で承認を確実に行ったり、製品の要件に関連付けられている自動テストを実行したりできます。 Azure Test Plans およびそれを使用して自動テストを実行する方法の詳細については、以下を参照してください。

2.7 コードベースのテスト ケース

まとめ

コードベースのテスト ケースは、製品のセキュリティと信頼性を維持するには不可欠な要素です。 これらのテストは小さくして、高速で実行できるようにします。また、並列で実行できるように互いに影響を及ぼさないようにする必要があります。 コードベースのテストは、開発者がコードに変更を加えるたびにいつでも、開発サイクルを遅らせる心配をせずに、ローカルの開発マシン上で簡単に実行できます。

種類、および他のセクションとの関係

一般的なコード ベースのテスト ケースの種類は次のとおりです。

  • 単体テスト
  • 複数の入力型を持つ関数をカバーするパラメーター化されたテスト
  • 各テスト コンポーネントを切り離しておくためのコンポーネント テスト
  • 他のサービスと通信するコードの一部を検証するモック テスト。テスト範囲をそのサービス自体に広げる必要はありません。

これらのテストは、記述された内部コードに基づいています。一方、ブラック ボックス テストは製品の外部機能要件に基づいています。

目標

目標は、これらのテストを通じて、コードに対して高レベルのテスト カバレッジを実現することです。 このカバレッジと、どこに欠落があるかを積極的に追跡する必要があります。 テストを追加してより多くのコード パスを実行することで、コードのセキュリティと信頼性に対する全体的な信頼度が向上します。

Visual Studio

Visual Studio のテスト エクスプローラー ツールを使用すると、これらのテストを頻繁に実行し、成功/失敗の割合やエラーの場所に関するフィードバックをすばやく簡単に取得できます。 テスト フレームワークの多くは CodeLens 機能もサポートしており、テスト自体の場所でテストの状態を確認できるため、テスト スイートの追加とメンテナンスが容易になります。 また、テスト エクスプローラーを使用すると、これらのテストを簡単に管理でき、テスト グループ、カスタム テストプレイリスト、フィルター処理、並べ替え、検索などが可能です。

詳細については、以下を参照してください:

Visual Studio には、コード カバレッジを追跡するツールも用意されています。 これらのツールを使用すると、コード変更が既存のテストで確実にカバーされるようにしたり、新しいコード パスやテストされていないコード パスをカバーする新しいテストを追加したりできます。 また、ツールにはコード カバレッジの割合も表示され、それが目標レベル以上に維持されるようにして、コード全体の品質に対する信頼度を確保します。

これらのツールの詳細については、コード カバレッジ テスト - Visual Studio (Windows) に関するページを参照してください

Azure の場合

Azure DevOps では、ビルド パイプライン プロセスの一環として、製品全体のコード カバレッジの結果を追跡することもできます。 詳細については、コード カバレッジの結果の確認 - Azure Pipelines に関するページを参照してください。

2.8 履歴テスト ケース

まとめ

履歴テスト ケース (別名: 回帰テスト ケース) は、以前の問題が再び表面化するのを防ぎ、製品の全体的なテスト カバレッジを増やします。 バグが修正されたら、対応するテスト ケースがプロジェクトによって確実に追加されるようにする必要があります。 時間の経過に伴い、修正が行われるにつれて、テスト スイートの全体的な堅牢性が向上し続け、信頼性とセキュリティがより確実なものになります。

主な性質、および他のセクションとの関係

バグ回帰をテストするので、これらのテストは、すばやく簡単に実行しなければなりません。そのため、コード ベースのテスト ケースと並行して実行することができ、製品の全体的なコード カバレッジに貢献します。 さらに、顧客の実際の例を使用して新しいテスト ケースの着想を得ることは、テストのカバレッジと品質を向上させるための優れた方法です。

Visual Studio

Visual Studio を使用すると、バグを修正するための変更を加えながら、テスト スイートに簡単にテストを追加できます。また、テストとコード カバレッジをすばやく実行して、新しいケースすべてが確実に考慮されるようにできます。 テストを記述するコードで問題追跡システムからバグ ID を参照することは、回帰テストを対応する問題に結びつける優れた方法です。 Azure Boards とテスト計画を Visual Studio と一緒に使用して、以下を行います。

  • テスト、テスト ケース、および問題を関連付けます。
  • 問題とそれに対応するテストのすべての側面を追跡します。

詳細については、以下を参照してください:

最終的に、これらのテストを、コード セクションをカバーすることになっている単体テスト領域に統合すると、テスト スイートが整理および管理しやすくなります。 テスト エクスプローラーのテスト グループを使用すると、同種のテストを効果的に追跡できます。 詳細については、テスト エクスプローラーを使用した単体テストの実行 - Visual Studio (Windows) に関するページを参照してください

2.9 ファジング

概要 ファジング (別名: ファジー テスト) は、プログラムへの入力として、無効なデータ、予期しないデータ、またはランダム データを提供する、自動化されたソフトウェア テスト手法です。 その後、クラッシュ、組み込みコードまたはコンパイラが挿入したコード アサーションの失敗、潜在的なメモリ リークなどの例外がプログラムによって監視されます。

ガイダンス

ファジングは、攻撃者が制御できる信頼できない入力を処理する可能性がある、すべてのソフトウェアで使用します。 新しいアプリケーションとそれに関連するテスト スイートを構築する場合、主要なモジュールについては、できるだけ早い段階でファジングを含めます。 ソフトウェアの一部で初めてファジングを実行すると、ほとんどの場合、それまで不明だった実際の脆弱性が明らかになります。 一度ファジングを開始したら、決して停止しないでください。

他のセクションとの関係

ファジングによってエラーが報告された場合は、当然、バグを示す再現可能なテスト ケースが必ず提供されます。 このテスト ケースを再現、解決し、履歴テスト ケースに追加できます。

両方のサニタイザー (Address Sanitizer (ASan)、ファジングなど) を使用する場合:

  • まず、サニタイザーを有効にして通常のテストを実行し、問題があるかどうかを確認します。その後、コードが sanitizer-clean になったら、ファジングを開始します。
  • C または C++ の場合、ランタイム アサーションとメタデータの挿入を自動化するコンパイラがあり、ASan を有効にします。 ASan 用にコンパイルされた場合、結果のバイナリはランタイム ライブラリとリンクされ、誤検知ゼロで 15 以上のカテゴリのメモリの安全性エラーを正確に診断できます。 C または C++ で、ソースがある場合は、LibFuzzer を使用します。それには、最初に ASan を有効にする必要があります。
  • Java、C#、Python、Rust などて記述されたライブラリの場合は、AFL++ フレームワークを使用します。

主な性質

  • ファジングは、静的なプログラム分析、徹底的な機能テスト、手動コード検査で見落とされることが多い脆弱性を検出します。
  • ファジングは、ソフトウェアのセキュリティと信頼性のバグを発見する効果的な方法です。そのため、Microsoft セキュリティ開発ライフサイクルでは、すべての製品の信頼されていないインターフェイスすべてに、ファジングが求められます (「脅威モデリング」も参照)。
  • 信頼されていない入力を処理する可能性があるソフトウェアには必ずファジングを使用します。
  • ファジングは、大規模なデータ パーサーを備えたスタンドアロン アプリケーションに効果的です。

Azure と GitHub CI/CD

LibFuzzer または AFL++ を使用する実行可能ファイルの継続的な作成をサポートするようにビルドを変更します。 OSS-Fuzz や OneFuzz などのサービスで、ファジングに必要な追加のコンピューティング リソースを追加できます。

2.10 Web アプリケーションのスキャン

まとめ

Windows 上の Microsoft Visual C++ のスコープ内では、Microsoft では次のことをお勧めします。

  • Web アプリケーションには TypeScript、JavaScript、ASP.NET を使用します。
  • C++ で Web 拡張機能を記述しないでください。 Microsoft は ActiveX を非推奨にしました。
  • コードが Emscripten/WASM にコンパイルされると、C++ ではなくなるため、その他のツールが適用されます。
  • Microsoft は、RESTler (ステートフル REST API ファザー) を提供しています。

概要と主な性質

Web アプリケーション スキャナーは、Web ページをクロールして Web アプリケーションを探索し、セキュリティの脆弱性がないかどうかを調べます。 このクロールでは、悪意のある入力が自動生成され、アプリケーションの応答が評価されます。 重要なのは、Web アプリケーションのスキャンでは、次をカバー/サポートする必要があることです。

  • 新しいアプリや不明なアプリを含め、ネットワーク内のすべての Web アプリをカタログ化し、アプリを少数から数千にスケーリングすること。
  • ソフトウェア バージョン、SOAP および REST API サービス、モバイル デバイスで使用される API に対する詳細スキャン。
  • DevOps 環境におけるアプリケーション開発とデプロイへのセキュリティ プリミティブの挿入。 これらのプリミティブはクローラーで動作します。
  • マルウェア検出。

2.11 付属ソフトウェアコンポーネントの確認

まとめ

他のプログラミング言語で記述されたコードと同じように C++ コードを処理し、会社で採用されているソフトウェア構成分析 (SCA) とオリジン分析 (OA) ツールを C++ コードに適用します。 ワークフローとセキュリティ スキャンは、CI/CD (継続的インテグレーションと継続的デリバリー) システムの一部として設計する必要があります。

アップストリームの防御

アップストリームの依存関係に対する攻撃のリスクを軽減するには、サード パーティのソース/コンポーネントを、SCA および OA ツールが実行されている、エンタープライズが管理する資産に保存する必要があります。

  • ツールは脆弱性が特定されたときにスキャンを実行し、アラートを生成する必要があります (パブリック データベースを含む)。Home | CVE など
  • アプリケーション/リポジトリに含まれるすべてのソフトウェア コンポーネントに対して静的分析を実行し、脆弱なコード パターンを特定します。

依存関係の防御

依存関係の監査を実行し、維持することで、その発生すべてが SCA および OA ツールによって考慮され、カバーされていることを検証します。

  • コンポーネントは定期的に監査し、最新の検証済みバージョンに更新する必要があります。
  • パッケージ フィードの依存関係。
  • SCA/OA ツールは、1 つのフィードからのすべてのパッケージ依存関係をカバーし、監査します。

SBOM

次のような依存関係が一覧表示された、製品の SBOM (ソフトウェア部品表) を生成します。

  • origin (URL (Uniform Resource Locator) など)
  • version
  • 一貫性 (SHA-256 ソース ハッシュなど)、および決定論的ビルドなどの一貫性を検証するためのその他の手段。
  • ソフトウェア依存関係内の、または OSS (オープンソース ソフトウェア) を含むビルドの一部として生成された SBOM ファイルを要求し、監査する。
  • Microsoft は SPDX (ソフトウェア パッケージ データ交換) バージョン 2.2 以降 | Linux Foundation を SBOM ドキュメント形式として標準化し、推奨している。
  • ビルドの決定性を使用すると、ビット単位の同一バイナリを個別に生成し、独立した整合性検証を提供できる。
    • ファースト パーティまたはサード パーティの再現性の構成証明
    • 信頼できる証明書ソースを介したバイナリ署名など、他の手法でも、バイナリ整合性をある程度保証できる。

その他のリソース

Microsoft ソリューションには、次のガイダンスと製品が含まれています。