こんにちは!日々の開発やインフラの保守、本当にお疲れ様です。セキュリティの世界へようこそ!
「なんだか小難しい用語がたくさんあって、どこから手をつければいいのかわからない……」
そんな風に不安を感じていませんか?大丈夫です。どんなにすごいエンジニアも、最初はみんな同じところで立ち止まりました。一歩ずつ、身近な例えから紐解いていけば必ず理解できるようになります。
今回は、WindowsなどのOSレベルでこっそり、しかし力強く私たちのプログラムを守ってくれている「Control Flow Guard(CFG)」という技術についてお話ししますね。関数ポインタやROP(Return-Oriented Programming)といった、ちょっと物騒な名前の裏側にある「泥棒と防犯の知恵」を一緒に覗いてみましょう!
—
1. そもそも「間接呼び出し」ってなに? 身近な例えで考えてみる
プログラムを作っていると、次に実行する処理をその場で直接決めるのではなく、「あ、今の状況ならこちらの関数(処理のまとまり)を呼ぼう」と、「行き先が書かれたメモ用紙(関数ポインタ)」を頼りにすることがよくあります。これをプログラミングの世界では「間接呼び出し」と呼びます。
これを私たちの日常生活に例えてみましょう。
あなたは大きなお屋敷に住んでいます。お屋敷の中にはたくさんの部屋があって、普段はリビング(通常の処理)にいますよね。
そして、誰かがインターホンを鳴らしたとき、執事(プログラム)はこう考えます。
「お客さまが来たら、応接室へご案内するメモを見て動こう」
この「応接室への行き先が書かれたメモ」が、まさに関数ポインタです。
泥棒の手口:メモをこっそり書き換える恐怖
もし、このお屋敷のセキュリティがガバガバで、悪意ある泥棒がこっそり忍び込み、「応接室へ行く」と書かれたメモを「金庫の前(危険な攻撃コード)へ行く」というメモに書き換えてしまったらどうなるでしょうか?
執事は疑うことなく、その偽のメモの通りにあなたを金庫の前へ案内してしまいます。これが、サイバー攻撃者がよく使う「メモリ破壊攻撃」や、そこから発展する「ROP(Return-Oriented Programming)」の恐ろしい仕組みです。プログラムが本来行くはずのない、危険な悪意のコードへ無理やりジャンプさせられてしまうのですね。
—
2. Control Flow Guard(CFG)という「超有能な門番」
「じゃあ、メモを勝手に書き換えられないようにすればいいんだ!」
そう気づいた頭のいいセキュリティの専門家たちが作った仕組みこそが、今回主役のControl Flow Guard(CFG)です。
CFGを身近な例えにすると、「お屋敷のあちこちに立っている、ものすごく厳格な専属の門番(ガードマン)」です。
CFGが有効な環境では、執事(プログラム)がメモを頼りにしてどこかの部屋へ移動しようとした瞬間、門番がすっと前に立ちふさがります。
そして、こう言うのです。
- 「ちょっとお待ちください。そのメモに書かれた行き先は、本当にあらかじめ許可されている安全な場所ですか?」
- 「ちょっと名簿を確認させてください……あ、この部屋はリストに載っていませんね。不審者です! 即座に通報してこのお屋敷(プログラム)を閉じます!」
この仕組みにより、たとえ泥棒がメモリを無理やり書き換えて「危ない場所へのメモ」を用意したとしても、門番(CFG)が「おい、そこは行き止まりだ!」と見破って、実行する前にプログラムを強制終了してくれるのです。お見事ですよね!
—
3. 開発現場でどう向き合う? CFGの設定と恩恵
私たち開発者がこのCFGの恩恵を受けるためには、特別なアルゴリズムを自分で一から書く必要はありません。現代のコンパイラ(Visual Studioなど)やOSの機能を「正しく有効化してビルドする」ことが、実務における最大の仕事になります。
例えば、Windows向けの開発でVisual Studioを使っている場合、プロジェクトの設定(プロパティ)でCFGを有効にします。
Visual Studio(MSBuild)での設定イメージ
プロジェクトファイル(.vcxproj)やコマンドラインでビルドする際、リンカーのオプションに次のようなフラグを指定します。
<!-- プロジェクトファイル(.vcxproj)のイメージ抜粋 -->
<ItemDefinitionGroup>
<Link>
<!-- Control Flow Guard (CFG) を有効にする設定 -->
<ControlFlowGuard>Guard</ControlFlowGuard>
</Link>
</ItemDefinitionGroup>
※コマンドライン(link.exe)で直接指定する場合は、/guard:cf というスイッチを渡します。
これだけで、コンパイラはプログラム内のすべての「間接呼び出し(関数ポインタの先)」をくまなくスキャンし、「ここからここにジャンプするのは安全なルートである」という正当な行き先リスト(Bitmap)をプログラムの中に埋め込んでくれます。
開発時のちょっとした注意点
「じゃあ、何も考えずに全部のプロジェクトで有効にすれば完璧だね!」
……と言いたいところですが、実務ではほんの少しだけ注意が必要です。
1. パフォーマンスのわずかなオーバーヘッド: 門番が毎回「この行き先、リストにある?」と確認するため、極限まで実行速度を求められる超高速な処理では、ごくわずかにパフォーマンスに影響が出る場合があります(とはいえ、近年のCPUではほとんど無視できるレベルです)。
2. 古いサードパーティ製ライブラリ: もし何十年も前に作られた古いDLLや、CFGに対応していない外部のライブラリと一緒に動かす場合、予期せぬクラッシュが起きることがあります。そのため、依存関係も含めてビルド全体でCFGの恩恵を受けられるように設計することが大切です。
—
4. まとめ:一歩ずつ、確実なセキュリティの土台を
いかがでしたでしょうか?
「Control Flow Guard(CFG)」という名前を聞くと、なんだか難解で近寄りがたい宇宙技術のように思えたかもしれません。でも、要するに「プログラムが勝手に変なところへジャンプしないように、お墨付きの行き先リストと照らし合わせて見張ってくれる強力な門番」のことです。
私たちが書くコードに、ほんの少しのコンパイル設定(防犯意識)をプラスしてあげるだけで、何重もの強固なセキュリティの網が私たちのアプリケーションをサイバー攻撃から守ってくれます。
「セキュリティは難しそう」と気負う必要はありません。こうした仕組みの裏側にあるストーリーを一つずつ知っていくことが、あなたのエンジニアとしての引き出しを何倍も豊かにしてくれますよ。
一歩ずつ、確実に、安全なソフトウェアの世界を一緒に作っていきましょう!
コメント