バッファオーバーフローとは ― 仕組みと対策
バッファ
バッファオーバーフローとは、領域を超えてデータを書き込む不具合
OSやブラウザの更新情報には、「バッファオーバーフローの脆弱性を修正」という一文がよく出てきます。バッファオーバーフローは、データを置く領域に入りきらない量を書き込み、隣にある別のデータを上書きする不具合です。攻撃に悪用されるので、見つかれば修正の対象になります。
データを置く領域をバッファと呼びます。被害は、上書きされる場所によって変わります。プログラムが異常終了するだけで済む場合もあります。一方、攻撃者にプログラムの制御を奪われ、思いどおりに動かされる場合もあります。
アメリカの国立標準技術研究所(NIST)の説明も同じです。システムをクラッシュさせることも、コードを挿入して制御を奪うこともできるとしています。
入力の長さを変えて、メモリの様子を見る
次の図は、関数が使う8バイトのバッファに入力を書き込む様子を示します。入力の長さと対策を切り替えると、どこまで上書きされるかを比べられます。
入力はバッファにぴったり収まります。隣の領域には何も書き込まれません。
入りきらない4バイトが、すぐ隣の退避領域に書き込まれます。呼び出し元に戻るときに、この壊れた値が使われます。誤動作や異常終了につながる恐れがあります。
あふれた8バイトが、退避領域と戻り先アドレスを上書きします。関数が終わると、プログラムは戻り先アドレスが指す場所へ進みます。
上書きは戻り先アドレスを越えて、呼び出し元の領域にまで広がります。戻り先アドレスが書き換わる点は、16バイトの場合と同じです。
入力の長さがバッファの大きさ以内なので、そのまま書き込まれます。
入力の長さが、バッファの大きさの8バイトを超えています。プログラムは、書き込む前にエラーとして断ります。バッファも隣の領域も、そのままです。
入力の長さが、バッファの大きさの8バイトを超えています。プログラムは、書き込む前にエラーとして断ります。バッファも隣の領域も、そのままです。
入力の長さが、バッファの大きさの8バイトを超えています。プログラムは、書き込む前にエラーとして断ります。バッファも隣の領域も、そのままです。
入力はバッファに収まり、カナリアの値は変わりません。関数が終わる直前の検査を通り、そのまま呼び出し元へ戻ります。
あふれた4バイトが、バッファの隣のカナリアを上書きします。関数が終わる直前の検査で、カナリアの値が変わっていることに気づき、プログラムを止めます。
上書きはカナリアを越えて、退避領域まで届きます。関数が終わる直前の検査で、カナリアの値が変わっていることに気づき、プログラムを止めます。
上書きは戻り先アドレスにまで届きます。ただし、検査は戻り先アドレスを使う前に行われます。カナリアの値が変わっているので、プログラムを止めます。
入力が8バイト以内なら、何も起きません。8バイトを超えた分は、バッファの外へ書き込まれます。メモリの並びは説明用に単純にしたもので、実際の並びはコンパイラや環境で変わります。
仕組み:スタックの上で、戻り先アドレスが隣にある
関数が呼び出されると、ローカル変数の領域が、スタックというメモリ領域に確保されます。ローカル変数とは、その関数の中だけで使う変数です。同じスタックには、関数が終わったあとに戻る場所を記録した値も置かれます。
この値を、戻り先アドレス、またはリターンアドレスと呼びます。情報処理推進機構(IPA)の説明でも、スタックにはローカル変数と戻り先アドレスの両方が置かれます。そのため、ローカル変数の領域があふれると、戻り先アドレスが容易に書き換わります。
書き込みは、アドレスの小さい側から大きい側へ進みます。戻り先アドレスは、バッファよりアドレスの大きい側にあります。そのため、あふれた分は、戻り先アドレスのある側へ広がります。前の図の最初の表示(対策なし・16バイト)が、この状態です。
関数が終わると、プログラムは戻り先アドレスが指す場所へ進みます。書き換えられた値が使われていない場所を指していれば、プログラムは異常終了します。入力した側が選んだ場所を指していれば、その場所の命令が実行されます。
IPAの説明では、攻撃者は、CPUが直接実行する機械語のバイト列を入力データとして送り込みます。同時に領域をあふれさせて、戻り先アドレスを書き換えます。標的になる領域には、スタックのほかにヒープと静的領域があります。スタックへの攻撃は最も実行が容易とされてきたので、ここではスタックを例にします。
あふれると何が起きるか
CWE(Common Weakness Enumeration)は、ソフトウェアの弱点を分類した一覧です。CWEは、バッファオーバーフローの結果を2つに分けています。1つはサービスの停止で、あふれによってプログラムがクラッシュしたり、無限ループに入ったりします。もう1つは不正なコードの実行で、制御を奪われます。
制御を奪われると、機密性・完全性・可用性のすべてが損なわれる恐れがあります。3つの性質は、情報セキュリティの3要素の記事で説明しています。戻り先アドレスまで届かない場合でも、近くの変数が書き換わって、プログラムの状態が変わることがあります。
対策:攻撃が成功する4つの段階を止める
IPAは、この攻撃が成功する条件を4つ挙げています。4つがすべて成り立ったときだけ攻撃が成功するので、1つでも止めれば、攻撃は失敗します。
ただし、IPAは、どの手段も100%完璧ではないと注意しています。そのため、1つの対策に頼らず、複数を重ねます。CWEも、ASLRやDEPのような実行環境の対策を、対策を重ねる多層防御の1つとしています。
開発者の対策:あふれさせない書き方にする
最も基本の対策は、書き込む前に、入力の長さがバッファに収まるかを調べることです。IPAは、領域の長さとデータの長さを常に意識してコードを書くよう求めています。CWEも、入力の長さ・型・範囲・形式を調べる検査を勧めています。
コピーに使う関数も、長さの上限を指定できるものを選びます。IPAは、長さを調べずにコピーする strcpy の使い方を、あふれが起こり得る例として挙げています。gets という関数は、あふれの原因としてよく知られています。この関数は、C言語の2011年の仕様から削除されました。
同じ2011年の仕様には、出力先の大きさを引数に取る gets_s や memcpy_s のような関数も定められています。CWEは、安全な文字列関数を備えたライブラリを使うことも、被害を抑える緩和策に挙げています。
言語の選び方も対策になります。CWEは、メモリを言語側が管理するJavaやPerlのような言語を、緩和策の1つに挙げています。ただし、CPUが直接実行する外部のコードを呼び出す部分には、あふれが残る場合があります。アメリカのCISAとNSAは、2025年6月に、メモリを安全に扱える言語への移行を進める手引きを公開しています。
コードを書いたあとは、ソースコード検査ツールや、あふれを検出するデバッグで問題を見つけます。完成したシステムを診断する方法は、脆弱性診断とペネトレーションテストの記事にあります。
実行環境の対策:カナリア・ASLR・DEP
プログラムにあふれが残っていても、実行環境の機能で攻撃を失敗させる対策があります。IPAが主なものとして挙げるのは、スタック保護・メモリ空間レイアウトのランダマイズ・データ実行防止です。CWEの現在の版も、同じ3つを緩和策に挙げています。
| 対策 | 働き | IPAの |
|---|---|---|
| スタックカナリア | 戻り先アドレスの手前に値を置く。関数が終わる直前に調べ、変わっていれば止める | あふれの |
| ASLR | スタックやヒープなどの置き場所を、実行のたびにランダムに変える | 配置の |
| DEP(NXビット) | データを置く領域の内容を、命令として実行させない | データ |
スタックカナリアは、戻り先アドレスの手前に置いた値です。あふれると、この値が先に書き換わります。関数が終わる直前に値を調べ、変わっていれば、戻り先アドレスを使う前にプログラムを止めます。GCCの -fstack-protector と、Visual C++の /GS が、この仕組みを備えています。
攻撃者が、置かれた値をあらかじめ知っていれば、同じ値で上書きして検査をすり抜けられます。そのため、実際のコンパイラやOSは、値を予測されにくくする工夫をしています。CWEによると、カナリアが検出できるのは一部のあふれだけです。結果も、多くの場合はプログラムの停止です。
ASLRは、スタックやヒープなどの置き場所を、実行のたびにランダムに変えます。攻撃は、書き換えたい場所や、制御を移したい場所のアドレスを想定して組み立てます。置き場所が変われば、その想定が外れます。CWEは、情報漏えいの脆弱性があると、ASLRを回避される場合があると注意しています。
DEPは、データを置く領域の内容を、命令として実行させません。この対策に使うCPUの機能を、AMDではNXビット、IntelではXDビットと呼びます。送り込まれたデータに制御が移っても、実行されずに止まります。CWEによると、DEPは近くの変数の上書きまでは防げません。
IPAの講座は2007年版で、挙げている実装はその時点のものです。仕組みの説明は、CWEの現在の版の緩和策と一致しています。
利用者の対策:修正プログラムを適用する
あふれの原因はプログラムの作りにあるので、利用者は自分で直せません。作り手が修正プログラムを公開したら、適用します。組織で修正プログラムを当てる流れは、脆弱性の記事を読むと分かります。
IPAは、パソコンやスマートフォンのOSと各種ソフトウェアを最新の状態にするよう呼びかけています。そのために、修正プログラムを適用します。そのほかの不正アクセスの手口と対策は、不正アクセスの記事にまとめています。
SQLインジェクションとの違い
SQLインジェクションとXSSは、入力が命令の一部として読まれる攻撃です。バッファオーバーフローは、入力が領域を超えて、隣にある別のデータを書き換える攻撃です。
SQLインジェクションやXSSの原因は、入力をSQL文やHTMLと区別せずに混ぜることです。バッファオーバーフローの原因は、入力の長さを確かめずにコピーすることです。そのため、SQLインジェクションなどの対策であるプレースホルダやエスケープは効きません。長さの検査や、あふれを起こさない書き方が対策になります。
練習問題(全7問)
正解:用意した
バッファは、
正解:戻り先アドレス
戻り先アドレスは、
正解:
領域の
正解:スタックカナリア
スタックカナリアは、
正解:スタックや
攻撃は、
正解:データを
DEPは、
正解:完全な
IPAは、
選んでから「採点する」を押すと、解説つきで答え合わせができます。
参考にした資料
- IPA セキュア・プログラミング講座2007年版 C/C++言語編 第10章(攻撃の種類、4つの要因と対策。2007年9月時点)
- IPA セキュア・プログラミング講座2007年版 C/C++言語編 第1章(スタックと、関数の戻り先の仕組み)
- MITRE CWE-120 Classic Buffer Overflow(原因、結果、緩和策)
- MITRE CWE-121 Stack-based Buffer Overflow(スタック上のバッファと戻り先アドレス)
- NIST CSRC 用語集 buffer overflow(定義)
- CISA・NSA Memory Safe Languages: Reducing Vulnerabilities in Modern Software Development(言語の選び方)
- IPA 日常における情報セキュリティ対策(OSとソフトウェアの更新)
関連記事
おすすめの本 PR
セキュリティとデータベースは、基本情報技術者試験の対策本でまとめて学べます。

