領域を超えるデータを送り込みプログラムを誤動作させる攻撃。
バッファオーバーフローとは、プログラムが用意した 入れ物(バッファ)の容量を超えるデータを送り込み、あふれた分が隣のメモリを上書きすることでプログラムを誤動作させる攻撃です。「バッファ」は一時的にデータをためる領域、「オーバーフロー」は「あふれること」を意味します。
身近な例で考えると、容量の決まったコップに、限界を超えて水を注ぎ続けるようなものです。水はコップからあふれ、隣に置いてあった大事な書類(重要なデータ)をびしょ濡れにして台無しにしてしまいます。プログラムが「もうこれ以上入らない」と確認しないのが原因です。
上のツールで▶ボタンを押すと、容量内の入力は安全に収まる一方、容量を超える入力が隣のメモリを破壊して制御を奪う流れと、境界チェックで防がれる様子を確認できます。
メモリの中では、バッファのすぐ隣に 戻りアドレス(=処理が終わったあとプログラムが次に戻る場所を示すデータ)などの大切な情報が並んでいます。バッファをあふれさせると、この大切な情報が上書きされてしまう、という点が攻撃の核心です。
攻撃は次の順で進みます。
・① あふれ:容量を超える大量のデータを送り込み、バッファからあふれさせる
・② 上書き:あふれた分が、隣にある戻りアドレスを塗りつぶす
・③ 書き換え:戻りアドレスを「攻撃者が用意したコードの場所」に巧妙に書き換える
・④ 乗っ取り:処理を終えたプログラムが攻撃者のコードへ飛び、コンピュータを自由に操られる
単に上書きするだけなら異常終了(誤動作)で済みますが、戻りアドレスを狙って書き換えると任意のコード実行(乗っ取り)という最悪の被害につながります。上のツールのoverwrite〜hijackステップで、隣のメモリが壊れて制御が奪われる様子を見てください。
根本的な対策は境界チェックです。これは データを書き込む前に「バッファの容量を超えていないか」を必ず確かめる処理です。超える分は捨てるかエラーにすれば、隣の大切なデータは決して上書きされません。あふれそうになったら蛇口を止める、というイメージです。
対策は、プログラムの作り方(セキュアプログラミング)とシステムの仕組みの両面から行います。
・長さを確認する関数を使う:書き込む長さを指定できる安全な関数を選び、長さチェックのない危険な関数を避ける
・入力値検証:受け取るデータの長さに上限を設けてはじく
・スタック保護(カナリア):戻りアドレスの手前に見張り役の値を置き、書き換えを検知する
・ASLR・DEP:メモリ配置をずらしたり、データ領域のコード実行を禁止して悪用を難しくする
基本は「容量を超えるデータは決して書き込まない」に尽きます。脆弱性が見つかったソフトは速やかに更新(パッチ適用)することも大切です。上のツールの最終ステップで、境界チェックによってあふれが防がれる様子を確認してください。
なぜあふれが「コンピュータの乗っ取り」につながるのか、その仕組みを理解するには「スタック」と「戻りアドレス」というキーワードが重要です。スタック(=関数を呼び出したときに使われる一時的なメモリ領域)には、バッファ(一時入力領域)と戻りアドレス(関数が終わったら次にどこへ戻るかを示す番地)が隣り合わせに置かれています。
住所録カードで例えると、「名前を書く欄」(バッファ)の隣に「次に向かうべき場所のメモ」(戻りアドレス)が書いてある状態です。名前欄に枠を超えて文字を書き続けると、隣のメモが消えて上書きされてしまいます。攻撃者はその「次に向かうべき場所」を自分のコードの場所に書き換えるため、プログラムが攻撃者の指示通りに動いてしまいます。
このことから、バッファオーバーフローは「コンピュータがどこへ戻るかを書き換える」攻撃とも言えます。上のツールのhijackステップで、戻りアドレスが上書きされて制御が移る瞬間を確認してください。
| 対策 | 仕組み | 防ぐのは誰の仕事 |
|---|---|---|
| 境界チェック | 書き込む前に長さを確認し超えたら拒否 | プログラマ |
| スタック保護(カナリア) | 戻りアドレスの手前に見張り番の値を置き改ざんを検知 | コンパイラ/OS |
| ASLR | メモリ配置をランダムにして攻撃者がアドレスを推測できなくする | OS |
| DEP/NX | データ領域でのコード実行を禁止する | OS/CPU |
バッファオーバーフロー対策は「プログラマが書くコードの工夫」と「OSや言語が提供する仕組み」の二段構えで行います。まず根本対策として、プログラマが書き込む前に長さを確認する「境界チェック」を行います。それに加えてスタック保護・ASLR・DEP/NXを組み合わせると、万が一バッファが溢れても被害を最小化できます。
ASLR(アドレス空間配置のランダム化)は「毎回メモリの置き場所を変える」仕組みです。攻撃者が「攻撃コードはアドレス0x8000にある」と書き込んでも、実際の配置が毎回違うため制御を奪うことが格段に難しくなります。DEP/NX(データ実行防止)は「データ領域でプログラムを動かせなくする」仕組みで、スタックに埋め込まれた攻撃コードを実行できなくします。
バッファ(=入れ物)とは、プログラムがデータを一時的に保存するためにメモリ上に確保した領域のことです。プログラムが「名前を入力してください」という欄を用意するとき、「最大20文字分の場所をメモリに取っておく」という処理が必要です。この「取っておいた場所」がバッファです。
なぜ容量(大きさ)を決めるのか:メモリは複数のプログラムや用途で細かく区切って使っています。バッファの隣にはまったく別の目的のデータがびっしり詰まっています。だから「バッファはここからここまで」と境界を決めて、その範囲だけを使うのが原則です。
ところがプログラムが「容量を確認しない」設計だと、入力が容量を超えてもそのまま書き込もうとします。あふれた分は隣のデータ(戻りアドレスなど)を上書きしてしまいます。このことからバッファオーバーフローは「境界を知らないプログラム」と「意地悪に長い入力」の組み合わせで起きる問題だとわかります。対策の第一歩は、書く前に必ず「まだ入る余地があるか」を確認することです。
| 攻撃 | 何を狙うか | 被害 | 根本対策 |
|---|---|---|---|
| バッファオーバーフロー | メモリの境界 | プログラム誤動作・乗っ取り | 境界チェック |
| SQLインジェクション | SQL命令の組み立て | DBの不正操作・漏えい | プリペアドステートメント |
| XSS | HTML出力の処理 | クッキー窃取・なりすまし | エスケープ処理 |
| DoS攻撃 | サーバの処理能力 | サービス停止(使えなくなる) | レート制限・負荷分散 |
バッファオーバーフローと他の攻撃を並べると、それぞれ「何が弱点か」が鮮明になります。バッファオーバーフローはプログラムのメモリ管理の不備を突く攻撃です。WebアプリのSQL処理やHTMLの出力を狙うSQLインジェクション・XSSとは、問題が起きる場所がまったく異なります。
なぜ区別が大切か:攻撃の種類が違えば対策も違います。たとえばプリペアドステートメントはSQLの問題を解決しますが、メモリの長さ管理には無効です。逆に境界チェックはSQLインジェクションを防げません。問題文で「どの攻撃を選ぶか・どの対策を選ぶか」が問われたとき、攻撃と対策を正しく組み合わせることが重要です。
なお、バッファオーバーフローはWebアプリよりもOSやデバイスドライバ(=ハードウェアをOSが扱うためのプログラム)など、低レベルなプログラムで特に問題になりやすい攻撃です。Web系(SQLi・XSS)との最大の違いは「ネットワーク越しにWebページを操作するのではなく、プログラム自体のメモリを直接壊す」点にあります。