41

「平均的な人は自由になりたくない。彼は単に安全になりたいだけだ。」-HLメンケン

私は非常に安全なCを書き込もうとしています。以下に、私が使用しているテクニックのいくつかをリストし、私が思うほど安全であるかどうかを尋ねます。私のコード/先入観を細かく裂くことを躊躇しないでください。最も些細な脆弱性を見つけたり、新しいアイデアを教えてくれたりする答えは、高く評価されます。

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

GNU Cプログラミングチュートリアルgetlineによると:

getline関数は、realloc関数を介して、必要に応じてメモリのブロックを自動的に拡大するため、スペースが不足することはありません。getlineが非常に安全である理由の1つです。[..] 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プラクティスに従い、自分自身や他の人にそれらをどのように正当化しますか?

資力:

リソースの多くは回答から借りたものです。

4

7 に答える 7

7

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

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

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

バッファー オーバーフローからの保護に関するこの IBM 開発者向け記事をご覧ください。

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

于 2010-01-05T18:35:45.253 に答える
4
  1. ストリームからの読み取り

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

    さらに(特殊文字に言及することで予想されるように)、getline()バッファのみを提供します。バッファの内容については保証しません (安全性は完全にアプリケーションに依存するため)。そのため、入力のサニタイズは、ユーザー データの処理と検証に依然として不可欠です。

  2. sscanf

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

  3. 一般的なコメント

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

これについて調べるには、David Wheeler の優れたセキュア コーディング サイト が最適です。

彼の無料のオンライン ブック「Linux および Unix 用のセキュア プログラミング HOWTO」は、定期的に更新される優れたリソースです。

また、彼の優れた静的アナライザーである FlawFinderを参照して、さらなるヒントを得ることができます。しかし覚えておいてほしいのは、自動化されたツールは、優れた経験豊富な目の代わりになるものではありません。

Flawfinder などの静的解析ツールは、単なるツールにすぎません。どんなツールも人間の思考に取って代わることはできません! 要するに「道具を持ったバカはやはりバカ」。分析ツール (欠陥発見器など) がセキュリティのトレーニングと知識の代わりになると考えるのは間違いです

個人的に David のリソースを数年間使用してきましたが、それらが優れていることがわかりました。

于 2010-01-05T18:41:33.073 に答える
1

Insecure Programming by Example
ブログでいくつかの回答を紹介

于 2010-01-05T18:46:56.537 に答える
1

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

于 2010-01-05T18:49:08.343 に答える
0

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)

最初の形式はbuf2 番目の引数を決定するために使用され、より明確であるためです。


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


于 2010-01-06T10:04:32.803 に答える