私が読んでいる本ではprintf
、単一の引数 (変換指定子なし) は非推奨であると書かれています。代用することをお勧めします
printf("Hello World!");
と
puts("Hello World!");
また
printf("%s", "Hello World!");
誰かがなぜprintf("Hello World!");
間違っているのか教えてもらえますか? 脆弱性を含んでいると本に書いてあります。これらの脆弱性は何ですか?
私が読んでいる本ではprintf
、単一の引数 (変換指定子なし) は非推奨であると書かれています。代用することをお勧めします
printf("Hello World!");
と
puts("Hello World!");
また
printf("%s", "Hello World!");
誰かがなぜprintf("Hello World!");
間違っているのか教えてもらえますか? 脆弱性を含んでいると本に書いてあります。これらの脆弱性は何ですか?
printf("Hello World!");
私見は脆弱ではありませんが、これを考慮してください:
const char *str;
...
printf(str);
str
書式指定子を含む文字列を指している場合%s
、プログラムは未定義の動作 (ほとんどの場合クラッシュ) を示しますがputs(str)
、文字列はそのまま表示されます。
例:
printf("%s"); //undefined behaviour (mostly crash)
puts("%s"); // displays "%s\n"
リテラル フォーマット文字列を使用した呼び出しは安全で効率的です。ユーザーが指定したフォーマット文字列を使用したprintf
呼び出しが安全でない場合に自動的に警告するツールが存在します。printf
最も深刻な攻撃は、フォーマット指定子printf
を利用します。%n
他のすべてのフォーマット指定子とは対照的に、たとえば%d
、%n
実際には、フォーマット引数の 1 つで指定されたメモリ アドレスに値を書き込みます。これは、攻撃者がメモリを上書きして、プログラムを制御できる可能性があることを意味します。ウィキペディア
に詳細が記載されています。
リテラル形式の文字列で呼び出した場合printf
、攻撃者は形式文字列に a を忍び込ませることができないため、%n
安全です。実際、gcc は への呼び出しをprintf
への呼び出しに変更するputs
ため、まったく違いはありません ( を実行してテストしてgcc -O3 -S
ください)。
ユーザーが指定した書式文字列を使用して呼び出すprintf
と、攻撃者が書式文字列に忍び込み、%n
プログラムを制御できる可能性があります。コンパイラは通常、これが安全でないことを警告します。 を参照してください
-Wformat-security
。また、ユーザーが指定した書式文字列を使用しても の呼び出しが安全であることを保証する、より高度なツールもありprintf
、正しい数と型の引数を に渡すかどうかをチェックすることさえあります
printf
。たとえば、Java にはGoogle の Error Prone
とChecker Frameworkがあります。
のかなり厄介な側面はprintf
、浮遊メモリの読み取りが限られた (そして許容可能な) 損害しか引き起こさないプラットフォームでさえ、書式設定文字の 1 つ%n
によって、次の引数が書き込み可能な整数へのポインターとして解釈され、これまでに出力された文字数が、それによって識別される変数に格納されます。私はその機能を自分で使用したことがなく、実際に使用する機能のみを含めるように記述した軽量のprintfスタイルのメソッドを使用することがあります(その機能または類似のものは含めません)が、受信した標準のprintf関数の文字列をフィードします信頼できないソースからのデータは、任意のストレージを読み取る能力を超えてセキュリティの脆弱性を露呈する可能性があります。
printf("Hello World\n")
同等のものに自動的にコンパイルされます
puts("Hello World")
実行可能ファイルを逆アセンブルして確認できます。
push rbp
mov rbp,rsp
mov edi,str.Helloworld!
call dword imp.puts
mov eax,0x0
pop rbp
ret
使用して
char *variable;
...
printf(variable)
セキュリティ上の問題が発生する可能性があります。printf をそのように使用しないでください。
したがって、あなたの本は実際には正しいです.1つの変数でprintfを使用することは推奨されていませんが、printf("my string\n")は自動的にputになるため、引き続き使用できます
printf()
gcc では、 およびをチェックするための特定の警告を有効にすることができますscanf()
。
gcc のドキュメントには次のように記載されています。
-Wformat
に含まれてい-Wall
ます。フォーマット チェックのいくつかの側面をより詳細に制御するには、オプション、、、、、、およびを使用できますが-Wformat-y2k
、 に は含まれていません。-Wno-format-extra-args
-Wno-format-zero-length
-Wformat-nonliteral
-Wformat-security
-Wformat=2
-Wall
-Wformat
オプション内で有効になっている は、これらの-Wall
ケースを見つけるのに役立ついくつかの特別な警告を有効にしません。
-Wformat-nonliteral
文字列リテラルをフォーマット指定子として渡さないと警告します。-Wformat-security
危険な構造を含む可能性のある文字列を渡すと、警告が表示されます。のサブセットです-Wformat-nonliteral
。-Wformat-security
有効にしたことで、コードベース (ログ モジュール、エラー処理モジュール、xml 出力モジュール) にあったいくつかのバグが明らかになったことは認めざるを得ません。すべての関数には、パラメーターに % 文字を指定して呼び出された場合に未定義の動作を実行できる関数が含まれていました。情報については、私たちのコードベースは現在約 20 年前のものであり、この種の問題を認識していたとしても、これらの警告を有効にしたとき、これらのバグの多くがまだコードベースに残っていることに非常に驚きました)。