質問

「普通の人は自由になりたくありません。彼はただ安全でありたいだけなのです。」 - H.L.メンケン

非常に安全な C を書こうとしています。以下に私が使用しているいくつかのテクニックを列挙し、それらが私が思っているほど安全であるかどうかを尋ねます。遠慮せずに私のコードや先入観をズタズタに引き裂いてください。たとえ最も些細な脆弱性を見つけたり、新しいアイデアを教えてくれたりする答えはすべて、 高く評価されます.

ストリームからの読み取り:

による GNU C プログラミング チュートリアル ゲットライン:

getline 関数は、 のブロックを自動的に拡大します 必要に応じて、reallocを介してメモリ 機能なので、不足することはありません スペースの -- getline が とても安全です。[..]getline は 入力ラインを安全に処理し、いいえ それがどれだけ長いかは関係ありません。

getline は次のようにする必要があると思います。 すべての入力の下で, を防ぎます。 バッファオーバーフロー ストリームからの読み取り時に発生しないようにします。

  • 私の推測は正しいでしょうか?これがエクスプロイトにつながる可能性のある入力や割り当てスキームはありますか?たとえば、ストリームの最初の文字が次のようなものである場合はどうなるでしょうか。 奇妙な制御文字, 、おそらく 0x08 BACKSPACE (ctl-H)。
  • getline が安全であることを数学的に証明するための作業は行われましたか?

Malloc は失敗時に Null を返します。

malloc でエラーが発生した場合、malloc は NULL ポインタを返します。ポインター演算を NULL (0x0) ポインターに適用できるため、これにはセキュリティ上のリスクが伴います。 推奨します

/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should handle 
       the error here as appropriate. */
} 

安全な sscanf:

使用するとき sscanf 私は、オーバーランの可能性を避けるために、抽出する文字列のサイズを入力文字列のサイズに割り当てることが習慣になっています。例えば:

const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];

sscanf(inputStr, formatStr, str1, str2);

str1 と str2 は inputStr のサイズであり、inputStr からは strlen(inputStr) を超える文字を読み取ることはできないため、次の点を考えると不可能に思えます。 考えられるすべての値 inputStr がバッファ オーバーフローを引き起こすには?

  • 私は正しいでしょうか?私が思いつかなかった奇妙なケースはありますか?
  • これを書くより良い方法はありますか?すでにそれを解決している図書館はありますか?

一般的な質問:

私は多数の質問を投稿しましたが、誰もそのすべてに答えてくれることは期待していません。質問は、私が探している答えのガイドラインのようなものです。セキュアな C の考え方を本当に学びたいと思っています。

  • 他にどのような安全な C のイディオムがあるでしょうか?
  • どのようなコーナーケースが必要か 常にチェックする?
  • これらのルールを適用する単体テストを作成するにはどうすればよいでしょうか?
  • テストしやすい方法、または正しいと証明できる方法で制約を強制するにはどうすればよいですか?
  • C 用の推奨される静的/動的分析技術またはツールはありますか?
  • あなたはどのような安全な C プラクティスに従っていますか?また、それを自分自身や他の人に対してどのように正当化しますか?

リソース:

リソースの多くは回答から借用しました。

役に立ちましたか?

解決

  1. ストリームからの読み取り

事実 getline() 「必要に応じてメモリのブロックを自動的に拡大する」とは、プロセスで利用可能なメモリを使い果たすほど長い入力を生成することは簡単であるため、これがサービス拒否攻撃として使用される可能性があることを意味します(または、さらに悪いことに、システムです!)。メモリ不足状態が発生すると、他の脆弱性も影響する可能性があります。メモリが少ない、またはメモリがない場合のコードの動作が良好になることはほとんどなく、予測するのが非常に困難です。私の意見では、特にセキュリティに敏感なアプリケーションでは、すべてに妥当な上限を設定する方が安全です。

さらに (特殊文字について言及することでご想像のとおり)、 getline() バッファを提供するだけです。バッファの内容については何の保証もありません (安全性は完全にアプリケーションに依存するため)。したがって、入力のサニタイズは依然としてユーザーデータの処理と検証に不可欠な部分です。

  1. sscanf

私は、正規表現ライブラリを使用することを好む傾向があり、ユーザー データに対しては、正規表現を使用するのではなく、非常に狭く定義された正規表現を使用します。 sscanf. 。こうすることで、入力時にかなりの検証を実行できます。

  1. 一般的なコメント

    • 入力処理のテストに使用できるランダム入力 (有効と無効の両方) を生成するファジング ツールが利用可能です
    • バッファ管理は非常に重要です。バッファオーバーフロー、アンダーフロー、メモリ不足
    • 競合状態は安全なコード内で悪用される可能性がある
    • バイナリ ファイルは、ヘッダーに無効な値や過大な値を挿入するように操作される可能性があるため、ファイル形式のコードは堅牢である必要があり、バイナリ データが有効であると想定しないでください。
    • 一時ファイルはセキュリティ問題の原因となることが多いため、慎重に管理する必要があります
    • コードインジェクションを使用すると、システムまたはランタイムライブラリの呼び出しを悪意のあるバージョンに置き換えることができます
    • プラグインは攻撃に巨大なベクトルを提供します
    • 一般原則として、ユーザー データ (またはアプリケーションの外部からのデータ) が処理、サニタイズ、検証されるまで無効で敵対的なものとみなされ、ユーザー データがアプリケーションに入る唯一の方法となる、明確に定義されたインターフェイスを持つことをお勧めします。

他のヒント

sscanf の例は間違っていると思います。そのように使用すると、依然としてオーバーフローする可能性があります。

読み取る最大バイト数を指定するこれを試してください。

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

バッファ オーバーフローに対する保護については、この IBM 開発記事をご覧ください。

テストに関しては、ランダムな長さのランダムな文字列を生成してプログラムに入力し、それらが適切に処理されることを確認するプログラムを作成します。

これについて検討を開始するのに適した場所は、 David Wheeler の優れたセキュア コーディング サイト.

彼の無料オンライン書籍「Linux および Unix 向けの安全なプログラミング HOWTO」は定期的に更新される優れたリソースです。

彼の優れた静的アナライザーもご覧になるとよいでしょう。 フローファインダー さらにヒントを得るために。しかし、覚えておいてください、自動ツールは、デビッドが非常にカラフルに表現したように、経験豊富な優れた目の代わりにはなりません。

Flawfinder などの静的解析ツールは単なるツールです。人間の思考に代わるツールはありません。要するに、 「道具を持った愚か者はやはり愚か者である」. 。分析ツール (欠陥検出ツールなど) がセキュリティのトレーニングや知識の代わりになると考えるのは間違いです

私は個人的に David のリソースを数年間使用してきましたが、それらが優れていると感じています。

Yannick Moy は、博士課程の在学中、C 用の Hoare/Floyd 最弱前提条件システムを開発しました。 それを CERT 管理の文字列ライブラリに適用しました. 。彼は多くのバグを発見しました (回想録の 197 ページを参照)。良いニュースは、彼の仕事にとって図書館がより安全になったことです。

Les Hatton の Web サイトもご覧ください。 ここ そして彼の本で より安全なC アマゾンから入手できます。

使用しないでください gets() 入力には使用します fgets(). 。使用するには fgets(), 、バッファが自動的に割り当てられる場合 (つまり、「スタック上」)、次のイディオムを使用します。

char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)

サイズを変更する場合でも、これは機能し続けます。 buf. 。私は次のような場合にこの形式を好みます。

#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)

最初の形式では buf 2 番目の引数を決定するためのものであり、より明確です。


の戻り値を確認してください fclose().


ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top