3

SQL インジェクションの問題を防ぐには、SQL クエリを作成する前に文字列値をエスケープする必要があることがわかっています。特に、ユーザーやその他の外部ソースからのものです。

このエスケープはいつ行うべきですか?値がプログラムに入ると、後で使用するためにエスケープされた値を保存する必要がありますか? または、エスケープされていない値を保存し、クエリが構成されているときにエスケープする必要がありますか? どちらのアプローチがより安全ですか? トレードオフとは何ですか?

1) 値を受け取ったときにエスケープする例:

$test = $mysqli->real_escape_string($_POST['test']);
. 
. 
. 
$query=" UPDATE * from test_panel where test='" . $test . "'";

2) クエリが構成されているときにエスケープする例:

$test = $_POST['test'];
. 
. 
. 
$query=" UPDATE * from test_panel where test='" . $mysqli->real_escape_string('$test') . "'";

これらのアプローチに違いはありますか?注射をしやすいのはどちらの方法ですか?また、それを防ぐ最も安全な方法は何ですか?

4

5 に答える 5

1

これは非常に興味深い質問ですが、答えはそれほど簡単ではありません。

real_escape_string を使用する適切なタイミングは? POST でデータが到着したとき、またはクエリを作成する直前に?

ない

少し説明させてください。

まず、用語を整理しましょう。質問の仕方に間違いが多い。

  1. real_escape_stringを使用したエスケープではなく、 formatについて話しましょう。エスケープの用途が非常に限られているという理由だけで、これは 1 種類の SQL リテラルの書式設定規則の一部にすぎません。他のタイプでは、異なるフォーマット規則が必要です。
  2. したがって、データが「POST に到着する」ときの書式設定は問題外です。どのフィールドがクエリのどの位置に入るかわからないため、どのルールを適用すればよいかわかりません。
  3. 最後になりましたが、POST も他の外部ソースも、クエリの書式設定とはまったく関係ありません。文字列リテラルをクエリに入れる必要がある場合は、source に関係なく、SQL 構文規則に従ってフォーマットする必要があります。数字などについても同様です。

したがって、データをフォーマットする必要がある唯一の適切な時期は、クエリを作成する直前です。

しかし、アプリケーション コードに real_escape_string() を直接適用することは、非常に悪い習慣です。

  1. 上で述べたように、文字列をフォーマットするにはエスケープだけでは不十分です。文字列の書式設定には、エスケープと引用の両方が含まれます。そのため、SQL クエリの文字列をフォーマットする機能が何であれ、常に一方ではなく両方のタスクを実行する必要があります。引用とエスケープの両方。これらの 2 つのルールは、一方を他方なしで適用するとまったく役に立たないためです。そのため、それらを 1 つの施設で結合することが不可欠です。
  2. データ型ごとに異なる書式設定規則があることを忘れないでください。数値はその型に明示的にキャストする必要がありますが、エスケープは役に立ちません。
  3. 手動でエスケープするのはばかげています。繰り返さ$mysqli->real_escape_string('$test')れると、コードが肥大化して読みにくくなります。データベース ドライバにすべての書式設定を依頼してみませんか? そのため、最新のテクノロジーに従う必要があります。プレースホルダーを使用してクエリ内のデータを表します。このようなプレースホルダーを処理している間、ドライバーはその場所にあるデータを自動的にフォーマットします。
    そして、それは安全で便利になります。

プレースホルダーを簡単に使用する方法は 2 つあります (読みやすさの点で手動エスケープよりも優れていない手動バインドなし)。

  • 準備されたクエリで使用する変数を渡すだけなので、PDO を使用します。

したがって、コードは次のようになります

$db->prepare("SELECT * from test_panel where test=?");
$db->execute(array($_POST['test']));

PDOはすべてのフォーマットを内部で行います

  • または独自のラッパーを発明してプレースホルダーを実装する

このように

function paraQuery()
{
    global $mysqli;

    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = $mysqli->real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = $mysqli->query($query);
    if (!$result)
    {
        throw new Exception($mysqli->error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

または、現在のクエリの場合:

$result = paraQuery("SELECT * from test_panel where test=%s", $_POST['test']);

見てください-それは短く、正気で安全になります。

于 2013-01-05T11:51:10.447 に答える
0

あなたはできるだけ遅くそれらを逃れるべきです。

これを実行する理由は、データが常に正確になるようにするためです。たとえば、最初に文字列をエスケープするstrlenと、(エスケープされていないバージョンと)同じではなくなり、シナリオによっては混乱やエラーが発生する可能性があります。


あなたの質問に対する本当の(imo)答えは、エスケープして使用することを忘れることですprepared statements'

于 2013-01-05T09:49:25.850 に答える
-1

クエリの前またはクエリで入力をエスケープするかどうかは関係ありません。

そして、はい、すべてを逃れる必要があります。自分のことを逃れたくない理由はありません。

文字列をエスケープしたくない場合は、それを実現します;-)

于 2013-01-05T09:42:31.140 に答える
-1

クエリを作成する際に実際に使用する前に、値をエスケープしないでください。これは、準備済みステートメント/PDO を使用するか、real_escape_string を使用してクエリを SQL 形式の文字列として作成するかに関係なく当てはまります。

データ値をサニタイズ/エスケープするのが早すぎて、その形式で保存すると、エラーが発生します。変数に顧客名や口座番号などのデータ値が含まれる場合、その変数にはエスケープされていない生の値が含まれている必要があります。

実際にクエリを作成するときだけ、すべての値がそのクエリに入れられるときに適切にエンコードされていることを確認する必要があります。

生データ値を保持する変数は、クエリを保持する変数とは異なるタイプの変数と考えてください。クエリ値を生データ値に直接割り当てたり、生データ値を組み合わせてクエリを作成したりしないでください。クエリの構成は、生データ値をエンコードする必要があることを知るためのトリガーです。

これを実践することで、このエンコーディングがどこで行われるかが明確になり、一貫性が保たれ、二重エンコーディングやエンコーディングの失敗の可能性が減ります。

反対のことをしようとしていると想像してください: すべての値を事前にエンコードします。多くの文字列値があり、それらのすべてがクエリで使用されるわけではないため、これは実際には不可能です。どこかに、ディスプレイへの出力とクエリの両方で使用される変数がある可能性があります。エスケープされた値を表示すると正しくありません。同様に、どの変数がクエリでの使用を意図しており、どの変数が他の SQL 以外の使用を意図しているかを追跡することも困難 (または不可能) です。

実際にクエリを作成するまで、すべての文字列変数に生の (エスケープされていない) 値を常に格納してください。プリペアド ステートメントにはエスケープされていない値が渡されるため、この方法はプリペアド ステートメントを使用する場合にも当てはまります。

于 2013-01-05T10:21:22.210 に答える
-2

まず、mysqliの実際のエスケープ文字列の2番目のパラメータとして接続を渡す必要があります。次に、プリペアドステートメントも使用する必要があります。

http://php.net/manual/en/mysqli.prepare.php

于 2013-01-05T09:49:29.030 に答える