C++のセキュリティ対策:押さえておきたい5つのポイント
DeveloperSteve Coochin
2022年7月19日
0 分で読めますC++は現代のテクノロジー業界に欠かせない存在となっています。デスクトップアプリケーションやサーバーアプリケーション、ゲーム、バーチャルリアリティ、IoTファームウェアなど、さまざまな用途で使われており、現代の多くのプログラミング言語の基盤にもなっています。
C++は1985年にC言語の拡張として初めてリリースされ、システムプログラミングや組み込みリソースの開発を念頭に設計されました。
広く普及し、多くのユーザーに利用される開発では、アプリケーション全体にセキュリティ対策を組み込むことが重要です。そこで今回は、C++で開発する際に押さえておきたいセキュリティ対策を見ていきましょう。
1. C++アプリケーションでの入力値の検証とサニタイズ
ユーザー入力を適切に処理することは、不正な侵入からアプリケーションを保護するうえで非常に重要です。C++はオブジェクト指向のメモリ操作に対応しているため、長年にわたって幅広く使われてきました。アプリケーション開発に特に役立つ一方で、悪意ある攻撃者に攻撃の足がかりとして悪用される可能性もあります。
変数を処理する方法の一つは、std:cin関数を使うことです。次のような方法があります。
この関数では、cin.clear()でエラーステータスフラグをクリアし、cin.ignore()でストリームに残っている文字を改行文字まで削除します。エラーメッセージがエンドユーザー向けの出力に表示されないよう、適切に処理することが重要です。エラーや警告のメッセージが、望ましくない攻撃経路を明らかにしてしまう可能性があります。
2. バッファオーバーフローとアンダーフロー
C++でユーザー入力を処理する際は、入力の長さ、範囲、形式、型を確認することが重要です。コアとなるオペレーティングシステムとより細かく連携するため、大量の入力データが関数に渡されると、バッファオーバーフロー攻撃が発生する可能性があります。
算術演算の結果が式の型で表現できる値を超えると、算術オーバーフローが発生することがあります。例を見てみましょう。
上の関数は、結果として0を出力します。xは符号なし整数で、0から65535までの値を表せるためです。この範囲を超えると、値をunit16_tで表現できず、オーバーフローが発生します。
オーバーフローやアンダーフローに対処する方法の一つは、変数の値が最小値から最大値の範囲内にあるかを処理時にチェックすることです。これにより、範囲の上限や下限を超える値を検出できます。テンプレートを使ってこの範囲を設定すれば、さまざまなケースで再利用できる汎用的なチェックを実装できます。
C++を使う際は、未定義動作を避けることが重要です。多くのコンパイラーは、アプリケーションのビルド時に未定義動作について警告します。たとえば、GCCやClangには-fsanitize=undefined flagがあります。
また、C++の関数セットにあるstrcpy()やstrcat()のような関数の使用にも注意してください。変数の処理には便利ですが、格納する値が保存先の型の容量を超えると、オーバーフローやアンダーフローが発生する可能性があります。
3. ファイルシステム操作の誤りによるパストラバーサルの脆弱性
ファイルの処理が不適切だと、ファイルパスのトラバーサル脆弱性につながることがあります。これはどのフレームワークでも危険ですが、C++のように細かな制御が可能な言語を使うオペレーティングシステムでは、特に注意が必要です。C++では、ファイルはストリームとして扱われ、ストリーム入出力を表す抽象化として機能します。ストリームは、コードの構造に応じて長さが定まらない文字の流れとして表現できます。
C++には、ifstream()、ofstream()、fstream()など、ファイルを扱うための手法が用意されています。これらの関数はfstreambaseクラスとiostreamクラス群を基にしており、fstreamで宣言されたファイルを管理するために設計されています。
それぞれのファイル処理クラスの違いは次のとおりです。
ofstream:出力ストリームを表すクラスです。ファイルを作成し、情報を書き込むために使います。ifstream:入力ファイルストリームを表すクラスです。ファイルから情報を読み取るために使います。fstream:ファイルの読み込みと書き込みの両方に使えるクラスです。
ファイルストリームを安全に扱うことが重要です。まずnew_file.open()で新しいファイルを開き、ファイルストリームにデータを書き込みます。最後に、処理が終わったらファイルを閉じ、処理中に発生する可能性のあるエラーを捕捉するcatch文を用意してください。
例:
4. コマンドインジェクションを防ぐため、system()やプロセス実行の呼び出しを最小限に抑える
他の多くの言語と同様に、コードベースの関数を使ったシステムとの直接的なやり取りには細心の注意が必要です。C++では、system()のような関数は避けるか、慎重に使用してください。こうしたシステム関連の関数は、アプリケーションが実行されるチップセットやハードウェアによって異なります。
また、アプリケーションをインストールして実行する方法によって、exec()、execp()、CreateProcess()などの実行可能な関数を利用できるようにすることも重要です。ただし、これらの関数も慎重に扱う必要があります。オペレーティングシステムの中核部分へのアクセスを可能にする場合があるため、特に注意してください。
C++アプリケーションは、重要なシステム情報に対してより細かなレベルでアクセスできるため、悪意ある活動や脆弱な経路が露見する範囲も広がる可能性があります。
5. 包括的な静的コード解析ツールでプロジェクトのライブラリをスキャンする
プロジェクトのライブラリや依存関係に潜む脆弱な経路を把握することは、セキュリティを維持するうえで欠かせません。脆弱な経路は、悪意ある攻撃者による不正な動作を許す可能性があります。さまざまなシステムやアーキテクチャ環境にインストールされるC++プロジェクトでは、特に注意が必要です。
静的コード解析(SCA)は、アプリケーションの開発中に潜在的な脆弱性を知らせてくれるだけでなく、デプロイするプロジェクトで脆弱性が見つかった場合にも通知してくれる便利な手法です。
SnykでC++のSCAスキャンを始める
Snykを使い始めるには、開発環境にSnyk CLIをインストールします。CLIはnpm、Yarn、スタンドアロンの実行ファイルなど、さまざまな方法でインストールできます。
次に、CLIをSnykアカウントに接続します。まだSnykアカウントをお持ちでない場合も、すぐに簡単に登録できます。登録ページにアクセスし、手順に沿って進めてください。
CLIをインストールしてSnykアカウントにサインインしたら、コマンドラインからsnyk authを実行してアカウントとの接続を完了します。認証リンクが表示され、認証ページがブラウザーで自動的に開きます。Authenticateをクリックすれば、Snyk CLIを使う準備は完了です。

最後に、スキャンするプロジェクトのルートディレクトリに移動し、snyk test –unmanagedを実行します。スキャンによって、オープンソースライブラリを含むプロジェクトのルートフォルダーが特定され、解析が実行されます。

完了すると、検出された内容の全体が表示され、さらに詳しく調査できます。特に便利なのは、スキャン結果のリンクから検出された問題を掘り下げ、脆弱性について詳しく把握できることです。

今すぐ登録してスキャンを始めましょう
開発者、DevOps担当者、テクノロジーに携わる人々にとって、セキュリティを優先し、開発中のアプリケーションを安全に保つためにできることをすべて行うことが重要です。アプリケーション開発にセキュリティを組み込むことで、エンドユーザーとそのデータを確実に保護できます。
キャプチャー・ザ・フラッグを始めよう
オンデマンドのバーチャル入門ワークショップを視聴して、キャプチャー・ザ・フラッグの課題の解き方を学びましょう。
