web-dev-qa-db-ja.com

sprintf / snprintfのどれがより安全ですか?

これらの2つのオプションのどちらを使用するのがより安全かを知りたいです。

_#define MAXLEN 255
char buff[MAXLEN + 1]
_
  1. sprintf(buff, "%.*s", MAXLEN, name)

  2. snprintf(buff, MAXLEN, "%s", name)

私の理解では、両方とも同じです。提案してください。

40
Arpit

指定した2つの式は、notと同等です。sprintfは、書き込む最大バイト数を指定する引数を取りません。単に、宛先バッファ、フォーマット文字列、および一連の引数を取ります。したがって、バッファがスペースを持っているよりも多くのバイトを書き込む可能性があり、そのために任意のコードを書き込みます。 %.*sは次の理由で満足のいく解決策ではありません。

  1. フォーマット指定子が長さを参照するとき、それはstrlenに相当するものを参照しています。これは、メモリ内の長さではなく、文字列の文字数の尺度です(つまり、nullターミネータはカウントしません)。
  2. フォーマット文字列の変更(たとえば、改行の追加)は、バッファオーバーフローに関するsprintfバージョンの動作を変更します。 snprintfを使用すると、フォーマット文字列または入力タイプの変更に関係なく、固定の明確な最大値が設定されます。
35
azernik

質問の簡単な例では、2つの呼び出し間のセキュリティに大きな違いはないかもしれません。ただし、一般的な場合、snprintf()はおそらくより安全です。複数の変換仕様を含むより複雑なフォーマット文字列を作成すると、特に以前の変換では必ずしも固定数が生成されないため、異なる変換全体でバッファ長を正確に考慮することは困難(またはほぼ不可能)になる可能性があります出力文字の。

したがって、snprintf()を使い続けます。

snprintf()のもう1つの小さな利点は(セキュリティ関連ではありませんが)、必要なバッファの大きさを教えてくれることです。

最後の注意-snprintf()呼び出しで実際のバッファサイズを指定する必要があります-ヌルターミネータのアカウンティングを処理します。

snprintf(buff, sizeof(buff), "%s", name);
10
Michael Burr

この節を読むまでは、snprintf()の方がはるかに優れていると思います。

https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html

簡単な要約:snprintf()は移植性がありません。システムからシステムへの動作の変更。 snprintf()の最も深刻な問題は、snprintf()を呼び出すだけでsprintf()が実装されている場合に発生する可能性があります。しかし、そうではないかもしれません。

そのため、今でもsnprintf()はより安全であると言っていますが、使用する際には注意が必要です。

5

最適かつ最も柔軟な方法は、snprintf!を使用することです。

size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, "%s", name);

C99では、snprintfは、'\0'を除く文字列に書き込まれたバイト数を返します。必要なバイト数よりも少ない場合、snprintfは、フォーマットを拡張するために必要なバイト数を返します('\0'を除く)。 snprintfに長さ0の文字列を渡すことにより、展開された文字列がどれくらいの長さであったかを事前に見つけ、それを使用して必要なメモリを割り当てることができます。

あなたのsprintfステートメントは正しいですが、安全目的のためにそれを使用するほど自信がありません(たとえば、1つの謎めいた文字がなく、あなたはシールドレスです) wait snprintfはANSI Cにありません。それは(だけ?)C99です。それが、もう1つを好む(弱い)理由かもしれません。

まあ。 strncpyも使用できますか?

例えば.

  char buffer[MAX_LENGTH+1];
  buffer[MAX_LENGTH]=0;             // just be safe in case name is too long
  strncpy(buffer,MAX_LENGTH,name);  // strncpy will never overwrite last byte
2
PypeBros

これら2つの間に重要な違いがあります-snprintf呼び出しは、正しい戻り値を見つけるためにname引数を最後までスキャンします(NULを終了します)。一方、sprintf呼び出しは、nameからAT MOST 255文字)を読み取ります。

したがって、nameが少なくとも255文字の非NUL終了バッファへのポインタである場合、snprintf呼び出しはバッファの最後から実行され、未定義の動作(クラッシュなど)を引き起こす可能性があります。 sprintfバージョンはサポートしません。

2
Chris Dodd

どちらも必要な結果を返しますが、snprintfはより一般的であり、指定された形式文字列に関係なく、文字列をオーバーランから保護します。

さらに、snprintf(またはその場合はsprintf)が最後の\0、文字列バッファを1バイト大きくする必要があります、char buff[MAXLEN + 1]

0
Eli Iser