コンピューター プログラミングでは、なんらかの操作または処理が必要な値は変数に格納されます。 変数は、単に名前と値のペアです。 人々は多くの命名スキームを使用していますが、通常、実際の名前にはほとんど制限がありません。 変数の値は、プログラムの途中で変更できます。
多くの最新のプログラミング言語では、数値からテキスト文字列、文字列と数値の配列に至るまで、変数値に関するすべてを変更できます。 多くの古いプログラミング言語では、数値の整数変数やテキストの文字列変数など、変数を型で定義する必要があり、これほどの柔軟性がありません。
柔軟性を提供するこの種の最新のプログラミング言語は、高水準言語と呼ばれます。 1 つの例は Python です。 これは、プログラミング言語のコンパイラーまたはインタープリターが、メモリー管理などの複雑な作業の多くを負担するためです。 これらの言語には、コードの記述をシンプルに保つのに役立つマシン コードからの抽象化レイヤーがあります。 ただし、このアプローチにはいくつかの制限があります。 古い低レベルのプログラミング言語でソフトウェアをコーディングする方が好まれることがよくあります。
ノート:すべての古い言語が低水準言語というわけではなく、すべての高水準言語が現代的というわけでもありません。 どちらも人気がありますが、一般的に高水準言語への移行がありました。
C などの低レベルのプログラミング言語は柔軟性がはるかに低く、同じ機能を実現するにはより多くの労力が必要です。 低水準言語の標準機能の 1 つは、メモリを直接管理する機能です。 これは便利な場合もありますが、バッファ オーバーフローとして知られるセキュリティ脆弱性クラスの一般的な原因でもあります。
バッファを埋める方法
変数の別の可能な名前はバッファーです。これは、他のデータに作用するためにのみ必要な、またはファイルに書き込まれる前に、データを一時的に格納する場所になる可能性があるためです。 C で変数を宣言するときは、変数に割り当てる必要があるメモリ容量を明示的に指定する必要があります。 これは一般に、変数を 1 つのことだけに使用する場合、特にその値も知っている場合は比較的簡単です。
すぐに追加する既知の値がない場合、またはコンテンツのサイズが変化する可能性がある場合、適切な量のメモリを確保することは少し複雑になります。 ありがたいことに、必要に応じてメモリ割り当てを調整することは可能ですが、そうしないと問題が発生します。
たとえば、Name と Age という 2 つの変数があるとします。 名前は文字列として定義されていますが、年齢は整数です。 また、Dave、Sean、Mary という短い名前の人しか知らなかったとします。 それぞれの名前を格納できるようにするために必要な ASCII 文字は 4 つだけですが、念のため、数字を 1 つ追加し、Name 変数の長さを 5 文字に設定します。 ありがたいことに、これにはジェームズ、バリー、ベッキーなどの名前が含まれています。
名前 |
年 |
|||||
B |
e | c | k | y | 3 | 2 |
D | a | v | e | 4 | 4 |
このシナリオでは、限られたメモリ スペースを効率的に使用するため、両方の変数がメモリ内に次々と格納されます。
バッファをオーバーフローさせる方法
問題は、予期せぬ事態が発生したときに発生し、より長い名前の人物が現れます。 ケイトリンは、たくさんの問題を起こそうとしている。 彼女が自分の名前を入力すると、ソフトウェアは言われたことを正確に実行し、彼女の名前を入力します。 デフォルトでは、C は、値が配置されている割り当てられたメモリに収まるかどうかを確認しません。また、スペースが不足しても切り捨てません。 何があってもそれを書くだけです。
名前 |
年 | |||||
ハ | a | 私 | t | l | y | n |
現在、書き込まれたデータは割り当てられたバッファーを超えて拡張され、オーバーフローして Caitlyn の年齢を上書きしています。 値を使用しようとしたものは何もないため、これはすぐには問題になりません。 残念ながら、値を読み取りたい場合は問題が発生します。 Caitlyn の名前を画面に出力してみましょう。名前の値が読み取られると、「Caitl」と出力されるためです。 名前を切るのは失礼ですが、大した問題ではありません。 年齢確認に関しては、もっと大きな問題があります。
ソフトウェアが、18 歳以上に制限されているものを購入できる年齢に達していることを確認するとします。 そうするために、この年齢> 18インチのようなチェックを実行するかもしれません. 数字としては、この操作は簡単で、年齢は 18 歳以上です。ベッキーとデイブの両方がパスしますが、他の人は若すぎる可能性があります。 しかし、ケイトリンには別のことが起こります。 ソフトウェアは、「yn」が 18 より大きいかどうかをチェックしようとします。ご想像のとおり、ソフトウェアはそれができないため、エラーをスローします。
エラーの重大度
この例のエラーの種類は比較的控えめです。 ソフトウェアによって処理され、年齢を編集して修正できる場合もあります。 最悪のシナリオでは、エラーが許容されない可能性があります。 この場合、プログラム全体がクラッシュします。
問題は、年齢のような単純なものが上書きされるという保証がないということです。 Name の後に続くのが pathToFileToDelete または pathToOtherSoftwareToRun の場合はどうでしょう。 これらの例では、ソフトウェアの機能を根本的に変更できます。 ハッカーがこれらの攻撃がどのように機能し、確実に実行するかを正確に知っていると想像してみてください。 それらを使用して、機密性の高い使用ファイルを削除したり、その他の潜在的に悪意のあるソフトウェアを実行したりできる場合があります。
このクラス全体の脆弱性は、重大度の高いセキュリティ脆弱性の宝庫です。 ほとんどはプログラムをクラッシュさせる機能を提供しますが、多くはさらに悪いものになる可能性があります。 バッファ オーバーフローの脆弱性の例は多数あり、コードの実行や権限昇格が可能になります。
バッファ オーバーフローの防止
一部のツールは、追加されるデータに基づいてメモリ割り当てサイズを修正したり、データが意図したメモリ割り当て内に収まるかどうかを単純にチェックしたりするなど、リスクを最小限に抑えます。 残念ながら、これらの手順はオプションで追加されるため、複雑になり、開発時間が長くなります。 「起こりうる最悪の事態」という理由でスキップされることもあれば、コードが 30 年前に書かれたものであるという理由で無視されることもあり、「壊れていない場合は修正しないでください」という理由で無視されます。
高水準言語では、ユーザーからメモリ割り当てを抽象化し、自動的に管理するため、この問題はありません。
結論
バッファ オーバーフローは、変数に割り当てられたメモリ領域が、変数内に配置されたデータによって超えられた場合のセキュリティ上の脆弱性のクラスです。 収まるように切り捨てられる代わりに、データはそのまま書き込まれ、影響を受ける変数の直後にメモリ内にあったものはすべて上書きされます。 多くの場合、これによりメモリが破損し、エラーがキャッチされないためにソフトウェアがクラッシュします。 ただし、一部のバッファ オーバーフローの脆弱性は、より危険な場合があります。 正しい (または間違った) 状況で、慎重に実行すると、バッファ オーバーフローを使用してソフトウェアの機能を変更することができます。多くの場合、悪意のあるものに変更されます。
バッファ オーバーフローの脆弱性は、不十分なメモリ割り当て管理に起因します。 これは、手動のメモリ管理を提供または必要とする低レベル言語でのみ発生します。 これらの言語は露出を防ぐためのツールを提供しますが、明示的に使用する必要があり、常にそうなるとは限りません。 高水準言語は、メモリ管理機能を開発者から完全に抽象化し、本質的に脆弱性のクラスを防ぎます。