8

ユーザーからのデータ入力を定期的に受け取る小さなWebアプリケーションを作成しています。データ入力が最初にクレンジングされていることを確認する方法を調査する場合、そのためには、プリペアドステートメントが進むべき道のように思われます。

しかし、私はこのSOの質問を見つけました、そして私のアプリケーションは(少なくとも私が知る限り)ページリクエストごとに複数のクエリを実行しないので、私が本当に必要とするのはパラメータへの値のバインドだけであるように思われますクエリ。

PDOとmysqliに関するPHPマニュアルを調べてきましたが、値が通常のクエリにバインドされている例は見つかりません。私が見つけたすべての例には$stmt->prepare、バインディングの前のどこかにあります。

ステートメントが「準備済み」であるかどうかは、データベースのサポートによって決定され、prepareステートメントは常にコード内にありますか?または、パラメータを直接にバインドする方法はあります$dbh->query(...)か?

準備を使用できない可能性があるかどうかを確認しようとしている理由を説明するために、投稿の前半でリンクしたSOの質問からのこのステートメントによるものです。

プリペアドステートメントを使用しないのはいつですか?db接続がなくなる前にステートメントを1回だけ実行する場合。

バインドされたクエリパラメータを使用しない場合(これは、ほとんどの人がプリペアドステートメントを使用して取得するものです)?

この

個人的には気にしません。疑似プリペアドステートメントは、おそらく提供される安全な変数の引用に役立つ可能性があります。

4

3 に答える 3

5

準備されていないクエリにパラメータをどのようにバインドしますか?

あなたはそうしない。パラメータ(つまり、特定の場所にある疑問符)を含むSQL文字列は、それらの疑問符をパラメータ値の挿入ポイントとして扱う前に、最初に解析(つまり準備)する必要があります。

したがって、を呼び出すprepare()前に常に呼び出す必要がありますbind()


パラメータ化されたステートメントは、SQLとプレースホルダーマーカーを含む文字列です(たとえば、疑問符ですが、データベースが異なれば、プレースホルダーも異なります)。

$sql = "SELECT user_id FROM user WHERE user_name = ?"

ここで、この場所に挿入するがあると仮定します。

$_POST["username"]

ステートメントを準備すると、大まかに言えば、疑問符に「値をここに挿入できる」という特別な意味が与えられます。つまり、プレースホルダーからパラメーターを作成します。

$stmt->prepare($sql)

値をパラメーターにバインドすると、パラメーターが特定の値に設定されます。

$stmt->bind_param("s", $_POST["username"])

これで、SQL文字列とユーザー指定の値が実際に相互に接触することなく、クエリを実行できます。これは重要なビットです。SQLとパラメータ値は別々にサーバーに送信されます。彼らは決してお互いに触れません。

$stmt->execute();

利点は次のとおりです。

  • 新しい値をパラメーターにバインドして、すべてを繰り返すことなくクエリを再実行できます(ループで役立ちます)。
  • どんな値$_POST["username"]が含まれていても、SQLインジェクションは不可能です。
于 2012-08-14T19:03:20.643 に答える
4

あなたはそうしない。理由は次のとおりです。

使用して$dbh->query(...) いる場合は、SQL文字列に補間されたパラメータを使用してSQLを呼び出すことができます。次のようなクエリを使用する

$dbh->query("INS INTO MY_TABLE_OF_NAMES ('$name');"); 

10年ほど前、これがほとんどのSQLの実行方法です。これは、特別な下位レベルのインターフェイスを必要とせずに、RDMSによってすでに実装されているSQLインターフェイスを使用して、データベースを呼び出す最も簡単な方法です。しかし、人々はこれがSQLインジェクションと呼ばれるもののために危険であることを発見しました。

http://en.wikipedia.org/wiki/Sql_injection

最も単純で最も一般的な例は、次のようになります。Webページで実行されるSQL呼び出しがあったとします。

 INS INTO MY_TABLE_OF_NAMES VALUE ('$name');

しかし、誰かがあなたのサイトに来て、そこに名前を入力しますbob'); DROP TABLE MY_TABLE_OF_NAMES;

突然、補間されたSQLステートメントは次のようになります。

INS INTO MY_TABLE_OF_NAMES VALUE ('bob'); DROP TABLE MY_TABLE_OF_NAMES; );

その後、bobをデータベースに挿入し、すべての名前を削除し、);Webサイトで実行されたときに末尾のエラーをスローします。

そこで、プリペアドステートメントが考案されました。文字列を文字列に直接補間する代わりに、文字を使用?して動的な値を示し、bind関数を使用して文字列を安全に挿入します。このように、巧妙な入力がデータベースエンジンによってSQLコードとして解釈されることはなく、サイトが騙されてやりたくないことを実行することはできません。prepareコマンドは、SQL文字列を取得し、SQLを少し取得して、下位レベルのデータベース言語にセミコンパイルし、aが使用されている場合は常に動的文字列のスペースを残します?。次に、Bindはそれらのオープンスペースの1つを取得し、エスケープされたASCIIにエンコードされたデータを入力して、SQLコードとして誤って解釈されないようにします。これらすべて?が満たされると、SQLをRDMSに送信して実行する準備が整います。

したがって、質問に答えるために、パラメータを単純なクエリにバインドすることは決してありません。単純なクエリで動的変数が必要な場合は、それらをSQL文字列に補間するだけです。しかし、これは危険です。プリペアドステートメントを使用すると、SQLステートメントをプリコンパイルしてから、動的パラメーターを安全にバインドして、安全な動的SQLを作成できます。SQLへのバインドは、純粋にプリペアドステートメントの構成です。

于 2012-08-14T19:33:03.050 に答える
0

バインドされたパラメーターを使用するには、プリペアドステートメントを使用する必要があります。これは、現在pdoとmysqliに実装されている方法です。特定のデータベース製品が、パラメータ化されたsql(プレースホルダーを使用するsqlテキスト)が最初に明示的なprepare呼び出しを行うことなくパラメーター値とともに送信される、ある種の通信プロトコルをサポートするかどうかはわかりませんが、pdoとmysqliは公開しません利用可能な場合は、この機能。それは確かにウェブアプリへの歓迎すべき機能でしょう。

pdoの場合、はい、呼び出したときにsqlステートメントが実際に準備されるかどうかは、$dbh->prepare($sql)データベースのサポートに依存します。pdoは、データベースがプリペアドステートメントをサポートしていない場合にプリペアドステートメントをエミュレートします。または、サポートするように構成されている場合は、常にプリペアドステートメントをエミュレートできます。実際、pdoはデフォルトでmysqlドライバーのプリペアドステートメントをエミュレートし、非常に長い間デフォルトでエミュレートしてきました。動的SQLを作成し、値を引用することで、それらをエミュレートします。この場合、を呼び出すと、sql(最終値がテキストに埋め込まれている)がデータベースに送信されます$stmt->execute()。はい、特定のシナリオでは、ここでSQLインジェクションが可能です。

于 2012-08-14T20:13:23.350 に答える