web-dev-qa-db-ja.com

より高速なSQLインジェクション防止

私はいたるところを見回しましたが、私の質問に対する答えが見つかりません。

私は最新のPHP=サーバーサイドスクリプティングに、MySQLをデータベースに使用しています。違いがある場合、文字セットは_utf8mb4_です。

これまでは、SQLインジェクションから身を守るために準備済みステートメントを使用していました。ただし、ほとんどのクエリは一度しか実行されないため、コードは実際にはslowです。代わりに$conn->query()を使用し、すべての変数に$conn->real_escape_string()を使用すると、処理が速くなります。

ただし、$conn->real_escape_string()は、呼び出しごとにラウンドトリップがあるため、速度も遅くなります。しかし、エスケープ関数はPHPでプログラムできるように思えるので、往復が必要な理由がわかりません。

ドキュメントから、関数は次のようになります:

エンコードされる文字は、NUL(ASCII 0)、\ n、\ r、\、 '、 "、およびControl-Zです。

_function sanitize($str)
{
    str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $str);
}
_

それが正しいエンコーディングであり、空でないことを確認したと仮定します。

これは、ドキュメントの1つにコメントとして投稿されました。ちなみに、_str_replace_はUnicodeで動作します。

でも、もう少し意味のあることを考えました。 this ページを見ると、これをlotにさらに引き伸ばすことができます。このようなステートメントの場合:

_$sql = "UPDATE ipsum SET lorem=$str WHERE id=1337";
_

私はただ行うことはできません(そして私はそれがUTF-8であることを事前に確認しています):

_function sanitize($str)
{
    return "'" .str_replace(array("\\", "'"), array("\\\\", "\\'"), $str) . "'";
}
_

そしてそれはSQLインジェクションを防ぎますか?何故なの?失敗する入力は考えられません。

そうでない場合、より良いSQLインジェクションから保護できるPHP関数がありますか?

前もって感謝します! SQLをテストできます ここ


UPDATE:予期した応答を受け取りました。明確にするために、私はあなたにあなたの古典的な説教をするようにあなたに求めているのではありません。よろしければYahoo!でお願いします。ここではなく答え。

私の方法(2つ目の方法)が安全でない(または安全である)理由を知りたいのですが。 _real_escape_string_または準備されたステートメントが実際にデータをサニタイズするために何を行うかについての議論を含めることも良いでしょう。 MySQLとPHPはどちらもオープンソースであり、これらの関数が呼び出されたときに何が起こっているかを誰かが知っていると確信しています。

文字列をサニタイズするために往復が必要な理由がわかりません。このようなメソッドをPHPに実装できないのはなぜですか?


UPDATE 2:すべてのユニコード文字(0x0000から0x1F77F、Wikipediaで見つかりました)を反復処理し、real_escape_string()は、単一の文字のみをエスケープし、フレーズはエスケープします(ドキュメントによると)、_utf8mb4_文字セットでは、変更される文字は次のとおりです。

unicode 0 =>\0

unicode 10 =>\n

ユニコード13 =>\r

ユニコード26 =>\Z

unicode 34 =>\"

ユニコード39 =>\'

ユニコード92 => \\

したがって、それは_utf8mb4_であっても、ドキュメントの記述と同じです(UTF-8が標準であるためだと思います)。では、なぜこれがPHPに実装できないのでしょうか。

これが PHPスクリプト で、これらのユニコードをすべて組み合わせてサニタイズします。これが SQLフィドル です。

10
Shahar

あなたのサニタイズ機能について-あなたの例を次のように変更した場合、あなたが提供した正確な例のエクスプロイトを生成することはできませんが:

$sql = "UPDATE ipsum SET price=$str WHERE id=1337";

10, otherColumn=1234または10;--の値が$strに渡された場合、問題が発生する可能性があります。独自のサニタイズ関数をロールしてコードで使用し始めた場合、このような脆弱性が出現するのは時間の問題だと思われます。あなたの質問であなたが提供した正確な例のエクスプロイトを見つけることができるかどうかを確認するために遊んでいきます。

とはいえ、独自のサニタイズ機能を導入することはお勧めしません。あなたや私より賢い人は、SQLインジェクションを防ぐために準備されたステートメントを使用して現在安全であると確信できるようにするために、現在受け入れられている業界標準の有効性をテストすることにかなりの努力を費やしました。自分で転がすことはほとんどの場合間違いです。

また、現在のプロジェクトよりも多くのトラフィックを処理し、より高いパフォーマンスを必要とする環境で、準備されたステートメントを使用している人がいると思います。

データレイヤーへのアクセスでパフォーマンスの問題が発生している場合は、独自のサニタイズ機能を構築するのではなく、パフォーマンスのチューニングに少し時間をかけることをお勧めします。

データレイヤーへのアクセスをパフォーマンスチューニングすることは、開発の不可欠で通常の部分です。独自のサニタイズをローリングすることはそうではありません。

14
Abe Miessler

まず、PHPにはすでに2番目の関数があり、それは addslashes() と呼ばれます。

ドキュメントは明示的に言っています:

データベースパラメータをエスケープするには、セキュリティ上の理由から、DBMS固有のエスケープ関数(MySQLの場合はmysqli_real_escape_string()、PostgreSQLの場合はpg_escape_literal()、pg_escape_string()など)を使用する必要があります。

DBMSにエスケープ関数がなく、DBMSが\を使用して特殊文字をエスケープする場合、この関数を使用できるのは、このエスケープメソッドがデータベースに適している場合のみです。データベースパラメータのエスケープにaddlashes()を使用すると、ほとんどのデータベースでセキュリティの問題が発生する可能性があることに注意してください。

最初の関数にも、MySQL構文にとって重要な 数文字 がありません。

これに頼るべきではないその他の重要な理由:

  • 特定のクエリについて、より効率的ではあるが包括的ではないサニタイズを回避できるとしましょう。作成したすべてのクエリに対する潜在的な攻撃ベクトルを十分に検討する必要がありますか?間違えるまでどれくらいかかりますか?

  • Mysql_real_escape_stringでプリペアドステートメントを使用する良い理由は、いつか何かをエスケープすることを必然的に忘れてしまうことです。よく知られているPHPプロジェクトの多くのSQLインジェクションは、誰かが何かをエスケープするのを忘れたことによって引き起こされました。それは誰にでも起こります。少なくともbindParam()の呼び出しを忘れた場合のクエリでは、クエリはt動作します。

パフォーマンスを向上させたい場合は、キャッシングを実装し、MySQLクエリを完全に回避します。

また、MySQLの最新バージョンを使用していることを確認してください。

5.1.17より前では、クエリキャッシュは準備されたステートメントには使用されません。 http://dev.mysql.com/doc/refman/5.1/en/query-cache.html

セキュリティに関しては、自分で考えないでください。

7
thexacre

車のブレーキを外して重量を節約できますか?まあ、あなたが孤立したサイトでのみ運転する場合and速度を最小限に抑えますand異常なことが起こらないようにします(車を使用している無知の人のように)、おそらく。

システムのあらゆる側面を制御し、何も見落とさない限り、メソッドは「機能」します。あなたはすでに2番目の要件を満たしていません(_NO_BACKSLASH_ESCAPE_設定を知らなかった)ので、何が今それで良いと思いますか?別の設定を見落としていないか?現在そして将来システムに取り組んでいる誰かが奇妙な制約を知っていて間違いを犯さないと約束できますか?

個人的に、私はそれにお金を賭けません。現実の世界でアプリケーションを作成するときは、たとえすべての設定を制御していなくても、誰かがミスを犯したとしても、細部を見落としても、allの条件下でそれらを機能させたいと思います。言い換えると、ソリューションはrobustである必要があります。これは、何十年にもわたるソフトウェア開発によって、人間は実際に誤りを犯しやすいことが示されているためです。準備されたステートメントは非常に堅牢で、mysql_real_escape_string()もかなり堅牢です。あなたの機能はそうではありません。全然。

また、準備されたステートメントとmysql_real_escape_string()の両方が「遅すぎる」とどのように結論付けたのでしょうか。非常に多くのユーザーがいて、いくつかの余分なラウンドトリップを心配する必要がある場合、最後に必要なのは自家製のセキュリティツールです。そして、あなたがそれほど多くのユーザーを持っていないなら、私はあなたが単に誇張していると思います。

5
Fleche

あなたのコードは失敗します:

  • mySQLサーバーのデフォルトの接続文字セットが厳密なASCIIスーパーセットでない場合、たとえばShift-JISの場合(マルチバイトシーケンスの2番目のバイトのバックスラッシュ用にバイトを密輸できる)) ;

  • mySQLサーバーがno_backslash_escape sql_modeオプションを使用するように構成されている場合(バックスラッシュエスケープはANSI標準ではないため、相互運用性のためと考えられます)。

これらの両方の場合に、潜在的なセキュリティ災害の結果を伴う文字列リテラルをエスケープできる入力があります。

したがって、現在の設定では問題ないかもしれませんが、他の誰かが他の場所で実行するのはかなり脆弱です。

4
bobince

一般的にエスケープには 問題があった があります。

何もエスケープする代わりに、 準備されたステートメント を使用してください。これは、SQLクエリにパラメーターを渡す唯一の健全な方法です。 SQLサーバーに値を渡すだけなので、これも最速だと確信しています。

2
kpcyrd