web-dev-qa-db-ja.com

strlcpyとstrlcatが安全でないと見なされるのはなぜですか?

strlcpystrlcatは、strncpystrncatの安全な代替品として設計されていることを理解しています。ただし、一部の人々はまだ自分が 安全ではなく、単に別のタイプの問題を引き起こす であると考えています。

誰かがstrlcpyまたはstrlcat(つまり、alwaysnullが文字列を終了する関数)の使用例を示すことができますかセキュリティの問題につながる可能性がありますか?

Ulrich DrepperとJames Antillはこれが真実であると述べていますが、例を提供したり、この点を明確にしたりすることはありません。

75
Anonymous

まず、strlcpystrncpyの安全なバージョンとして意図されたことがありません(およびstrncpystrcpyの安全なバージョンとして意図されたことがありません)。これら2つの機能はまったく無関係です。 strncpyは、C文字列(つまり、nullで終わる文字列)とはまったく関係のない関数です。名前にstr...プレフィックスが付いているという事実は、歴史的な過失にすぎません。 strncpyの歴史と目的はよく知られており、十分に文書化されています。これは、Unixファイルシステムの一部の歴史的なバージョンで使用されている、いわゆる「固定幅」文字列(C文字列ではない)を操作するために作成された関数です。今日、一部のプログラマはその名前に混乱し、strncpyが何らかの形で長さの制限されたC文字列コピー機能(strcpyの「安全な」兄弟)として機能すると想定しています。完全なナンセンスで、悪いプログラミング慣行につながります。現在の形式のC標準ライブラリには、長さ制限のあるC文字列のコピー機能はありません。これは、strlcpyが収まる場所です。strlcpyは、実際には、C文字列を操作するために作成された真の制限長コピー関数です。 strlcpyは、制限された長さのコピー関数が行うべきことをすべて正しく行います。唯一の批判は、残念ながら標準ではないということです。

第二に、strncatは、Cストリングで機能し、制限された長さの連結を実行する関数です(実際、strcatの「安全な」兄弟です)。この関数を適切に使用するために、プログラマーは特別な注意を払う必要があります。この関数が受け入れるサイズパラメーターは、実際に結果を受け取るバッファーのサイズではなく、残りの部分のサイズ(ターミネーター文字)暗黙的にカウントされます)。そのサイズをバッファのサイズに関連付けるために、プログラマはstrncatを批判するためによく使用される追加の計算を実行することを忘れないでください。 strlcatはこれらの問題を処理し、インターフェイスを変更して、追加の計算が不要になるようにします(少なくとも呼び出しコードでは)。繰り返しますが、これを批判できる唯一の根拠は、関数が標準ではないということです。また、strcatグループの関数は、再スキャンベースの文字列連結というアイデアの限られた使いやすさのため、プロのコードではあまり見られないものです。

これらの機能がどのようにセキュリティの問題につながる可能性があるのか​​について...単にできません。 C言語自体が「セキュリティ問題につながる」よりも、セキュリティ問題につながることはありません。かなり長い間、C++言語はJavaの奇妙な風味へと発展する方向に進まなければならないという強い感情がそこにありました。この感情はC言語の領域にも波及し、C言語の機能やC標準ライブラリの機能に対する無知で強制的な批判を招くことがあります。この場合も同様に対処しているのではないかと疑っていますが、事態がそれほど悪くないことを願っています。

105
AnT

Ulrichの批判は、プログラムによって検出されない文字列の切り捨てが、誤ったロジックを通じてセキュリティの問題につながる可能性があるという考えに基づいています。したがって、セキュリティを確保するには、切り捨てを確認する必要があります。文字列連結に対してこれを行うとは、次の行に沿ってチェックを行うことを意味します。

if (destlen + sourcelen > dest_maxlen)
{
    /* Bug out */
}

プログラマーが結果をチェックすることを覚えている場合、strlcatはこのチェックを効果的に行います-したがって、canは安全に使用できます

if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
    /* Bug out */
}

Ulrichのポイントは、destlensourcelenの周りに(またはそれらを再計算する必要があるので、strlcatが効果的に行う)、より効率的なとにかくmemcpy

if (destlen + sourcelen > dest_maxlen)
{
    goto error_out;
}
memcpy(dest + destlen, source, sourcelen + 1);
destlen += sourcelen;

(上記のコードでは、dest_maxlenは、destに格納できる文字列の最大長です。destバッファのサイズより1つ小さいです。dest_bufferlendest buffer)のフルサイズです。

30
caf

strcpy()は危険です。代わりにstrncpy()を使用してください」(またはstrcat()などに関する同様の文言ですが、strcpy()ここで私の焦点として)、彼らはstrcpy()でチェックする境界がないことを意味します。したがって、文字列が長すぎると、バッファオーバーランが発生します。彼らは正しいです。この場合にstrncpy()を使用すると、バッファーオーバーランが防止されます。

strncpy()は実際にはバグを修正しないと感じています。優れたプログラマーが簡単に回避できる問題を解決します。

Cプログラマーとして、文字列をコピーする前にmust宛先サイズを知っている必要があります。それは、strncpy()strlcpy()の最後のパラメーターの仮定でもあります。そのサイズを指定します。文字列をコピーする前に、ソースのサイズを知ることもできます。次に、宛先が十分に大きくない場合、_strcpy()を呼び出しません。バッファを再割り当てするか、何か他のことを行います。

strncpy()が気に入らないのはなぜですか?

  • strncpy()はほとんどの場合、悪い解決策です。文字列は予告なしに切り捨てられます。これを自分で理解するために余分なコードを書いてから、私がしたい行動を取ります。何らかの機能で何をすべきかを決めるのではなく。
  • strncpy()は非常に非効率的です。宛先バッファのすべてのバイトに書き込みます。目的地の終わりに数千の_'\0'_は必要ありません。
  • 宛先が十分に大きくない場合、終了_'\0'_を書き込みません。とにかく、あなた自身でそうしなければなりません。これを行う複雑さは、面倒な価値はありません。

ここで、strlcpy()に行きます。 strncpy()からの変更により改善されますが、_strl*_の特定の動作がそれらの存在を保証するかどうかはわかりません。あなたはまだ宛先サイズを知る必要があります。宛先のすべてのバイトに書き込む必要がないため、strncpy()よりも効率的です。ただし、*((char *)mempcpy(dst, src, n)) = 0;を実行することで解決できる問題は解決します。

strlcpy()またはstrlcat()がセキュリティ問題につながる可能性があるとは誰も言わないと思います。その一部ではなく、書き込まれる完全な文字列。

ここでの主な問題は、コピーするバイト数ですか?プログラマーはこれを知っている必要があり、知らない場合、strncpy()またはstrlcpy()は彼を保存しません。

strlcpy()およびstrlcat()は標準ではなく、ISO CもPOSIXでもありません。したがって、ポータブルプログラムでの使用は不可能です。実際、strlcat()には2つの異なるバリアントがあります。 Solarisの実装は他のものとは異なります 長さが0のEdgeケースの場合。

20
Alok Singhal

Ulrichや他の人たちは、それが誤った安心感を与えると思うと思います。誤って文字列を切り捨てるcanは、コードの他の部分にセキュリティ上の影響を及ぼします(たとえば、ファイルシステムパスが切り捨てられた場合、プログラムは目的のファイルで操作を実行していない可能性があります)。

11
jamesdlin

strl関数の使用に関連する2つの「問題」があります:

  1. 切り捨てを避けるために戻り値を確認する必要があります。

C1xの標準ドラフトライターとDrepperは、プログラマは戻り値をチェックしないと主張しています。 Drepperは、長さを何らかの方法で知ってmemcpyを使用し、文字列関数を完全に使用しないようにすべきだと述べています。標準委員会は、_TRUNCATEフラグ。アイデアは、人々がif(strncpy_s(...))を使用する可能性が高いということです。

  1. 非文字列には使用できません。

一部の人々は、偽のデータを供給しても文字列関数は決してクラッシュすべきではないと考えています。これは、通常の状態ではセグメンテーション違反になるstrlenなどの標準機能に影響します。新しい標準には、このような多くの機能が含まれます。もちろん、チェックにはパフォーマンス上のペナルティがあります。

提案されている標準関数の利点は、strl関数を使用して、見落としたデータ量を知ることができることです。

7
jbcreix

strlcpystrlcatinsecureであるとは思わないか、少なくともそれが理由ではないglibcには含まれていません-結局、glibcにはstrncpyとstrcpyさえ含まれています。

彼らが得た批判は、彼らは非効率的であり、安全ではないと言われています。

Secure Portability Damien Millerの論文によると:

Strlcpyおよびstrlcat APIは、ターゲットバッファーの境界を適切にチェックし、すべての場合でヌル終端し、ソース文字列の長さを返します。これにより、切り捨ての検出が可能になります。このAPIは、OpenBSD(元々)、Sun Solaris、FreeBSD、NetBSD、Linuxカーネル、rsync、GNOMEプロジェクトなど、ほとんどの最新のオペレーティングシステムと多くのスタンドアロンソフトウェアパッケージで採用されています。注目すべき例外は、GNU標準Cライブラリ、glibc [12]です。そのメンテナーは、これらの改良されたAPIの組み込みを断固として拒否し、ラベル付けします[4]、以前の証拠にもかかわらず、それらが置き換えるAPIよりも高速であることがほとんどである[13]。その結果、OpenBSDポートツリーに存在する100以上のソフトウェアパッケージが維持されます独自のstrlcpyおよび/またはstrlcatの置換または同等のAPI-理想的な状況ではありません。

それがglibcで利用できない理由ですが、Linuxで利用できないことは事実ではありません。 Linuxのlibbsdで利用可能です:

これらは、DebianおよびUbuntuおよびその他のディストリビューションにパッケージ化されています。また、コピーを取得してプロジェクトで使用することもできます-それは短く、許容ライセンスの下です:

4
rsp

セキュリティはブール値ではありません。 C関数は、完全に「安全」または「安全でない」、「安全」、または「安全でない」わけではありません。誤って使用すると、Cでの単純な割り当て操作は「安全でない」可能性があります。 strlcpy()およびstrcat()は、プログラマが正しい使用法の必要な保証を提供するときにstrcpy()およびstrcat()を安全に使用できるように、安全に(安全に)使用できます。

これらすべてのC文字列関数(標準および非標準)の主なポイントは、安全/安全に使用するレベルeasyです。 strcpy()とstrcat()は安全に使用するのは簡単ではありません。これは、Cプログラマーが長年にわたって間違っており、厄介な脆弱性と悪用が続いた回数によって証明されています。 strlcpy()およびstrlcat()およびその点で、strncpy()およびstrncat()、strncpy_s()およびstrncat_s()は、bitより簡単です安全に使用するが、それでも重要なことです。彼らは安全ではありませんか?誤って使用された場合、memcpy()以上のものはありません。

1
rswindell