web-dev-qa-db-ja.com

なぜ私たちは常にSQLステートメントでパラメーターを使用することを好むのですか?

私はデータベースを扱うのは非常に新しいです。これで、SELECTUPDATEDELETE、およびINSERTコマンドを作成できます。しかし、私たちが書くことを好む多くのフォーラムを見てきました:

SELECT empSalary from employee where salary = @salary

...の代わりに:

SELECT empSalary from employee where salary = txtSalary.Text

なぜパラメーターを常に使用することを好むのですか?

最初の方法の使用法と利点を知りたかった。 SQLインジェクションについても聞いたことがありますが、完全には理解していません。 SQLインジェクションが私の質問に関連しているかどうかさえ知りません。

102
Sandy

パラメータを使用すると、デスクトッププログラムやWebサイトなどのプログラムインターフェイスと組み合わせてデータベースを使用するときに、SQLインジェクション攻撃を防ぐことができます。

この例では、ユーザーはtxtSalaryのステートメントを作成することにより、データベースでSQLコードを直接実行できます。

たとえば、0 OR 1=1を記述する場合、実行されるSQLは次のようになります。

 SELECT empSalary from employee where salary = 0 or 1=1

これにより、すべてのempSalariesが返されます。

さらに、ユーザーはデータベースを削除するなど、データベースに対してはるかに悪いコマンドを実行する可能性があります0; Drop Table employeeを書き込んだ場合:

SELECT empSalary from employee where salary = 0; Drop Table employee

その後、テーブルemployeeが削除されます。


あなたの場合、.NETを使用しているようです。パラメータの使用は次のように簡単です。

C#

string sql = "SELECT empSalary from employee where salary = @salary";

using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
    var salaryParam = new SqlParameter("salary", SqlDbType.Money);
    salaryParam.Value = txtMoney.Text;

    command.Parameters.Add(salaryParam);
    var results = command.ExecuteReader();
}

VB.NET

Dim sql As String = "SELECT empSalary from employee where salary = @salary"
Using connection As New SqlConnection("connectionString")
    Using command As New SqlCommand(sql, connection)
        Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
        salaryParam.Value = txtMoney.Text

        command.Parameters.Add(salaryParam)

        Dim results = command.ExecuteReader()
    End Using
End Using

2016-4-25を編集:

George Stockerのコメントに従って、サンプルコードをAddWithValueを使用しないように変更しました。また、IDisposablesをusingステートメントでラップすることをお勧めします。

114
Chad Levy

確かに、これは SQLインジェクション に関連しています。これは、malicioiusユーザーがデータベースに対して任意のステートメントを実行できる脆弱性です。この昔のお気に入り XKCD comic は概念を示しています:

Her daughter is named Help I'm trapped in a driver's license factory.


あなたの例では、単に使用する場合:

var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query

SQLインジェクションに対してオープンです。たとえば、誰かがtxtSalaryと入力するとします。

1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.

このクエリを実行すると、SELECTUPDATEまたはDROP、またはそれらが望むものが実行されます。最後の--は、クエリの残りの部分をコメントアウトするだけです。これは、txtSalary.Textの後に何かを連結している場合に攻撃に役立つでしょう。


正しい方法は、パラメーター化されたクエリを使用することです(例:(C#):

SqlCommand query =  new SqlCommand("SELECT empSalary FROM employee 
                                    WHERE salary = @sal;");
query.Parameters.AddWithValue("@sal", txtSalary.Text);

これにより、クエリを安全に実行できます。

他のいくつかの言語でSQLインジェクションを回避する方法については、 bobby-tables.comSOユーザー によって管理されているWebサイトを確認してください。

68

他の回答に加えて、パラメータを追加する必要があります。これは、SQLインジェクションの防止に役立つだけでなく、クエリのパフォーマンスを向上させることができます。パラメータ化されたクエリプランをキャッシュし、クエリの繰り返し実行時に再利用するSQLサーバー。クエリをパラメーター化しない場合、sqlサーバーは、クエリのテキストが異なる場合、各クエリ(一部除外を含む)実行で新しいプランをコンパイルします。

クエリプランのキャッシュの詳細

5
Oleg

私の最初の行く の2年後、私は再犯しています...

なぜパラメーターを好むのですか? SQLインジェクションは明らかに大きな理由ですが、SQLに戻ることをひそかに望んでいる可能性があります言語として。文字列リテラル内のSQLは既に奇妙な文化的慣習ですが、少なくともリクエストを管理スタジオにコピーアンドペーストできます。 SQLに条件および制御構造がある場合、ホスト言語の条件および制御構造で動的に構築されたSQLは、レベル0の野barです。アプリが生成するSQLを確認するには、デバッグで、またはトレースを使用してアプリを実行する必要があります。

パラメータだけで停止しないでください。最後までQueryFirst(免責事項:私が書いた)を使用します。 SQLは。sqlファイル内にあります。テーブルと列の構文検証とIntellisenseを使用して、すばらしいTSQLエディターウィンドウで編集します。特別なコメントセクションでテストデータを割り当て、[再生]をクリックして、ウィンドウ内でクエリを実行できます。パラメータの作成は、SQLに「@myParam」を入れるのと同じくらい簡単です。その後、保存するたびに、QueryFirstはクエリのC#ラッパーを生成します。 Execute()メソッドの引数として、強く型付けされたパラメーターがポップアップ表示されます。結果は、IEnumerableまたは厳密に型指定されたPOCOのリストで返されます。POCOは、クエリによって返される実際のスキーマから生成された型です。クエリが実行されない場合、アプリはコンパイルされません。 dbスキーマが変更され、クエリは実行されても一部の列が消えた場合、コンパイルエラーはコード内の行を指しており、不足しているデータにアクセスしようとします。他にも多くの利点があります。 なぜ他の方法でデータにアクセスしたいのですか?

3
bbsimonbb

SQLで任意のWordに@記号が含まれている場合、変数であることを意味し、この変数を使用して値を設定し、同じSQLスクリプトの番号領域で使用します。多くのスクリプトで同じタイプと名前の。ストアドプロシージャはプリコンパイルされたクエリであるため、この変数をストアドプロシージャロットで使用し、スクリプト、デスクトップ、およびWebサイトからこれらの変数に値を渡して詳細情報を取得できます Declare Local VariableSqlストアドプロシージャ および SQLインジェクション

SQLインジェクションから保護 も読んで、データベースを保護する方法をご案内します。

質問があればコメントしてください。

3
Emaad Ali

他の回答では、パラメーターが重要である理由が説明されていますが、欠点もあります。 .netには、パラメーターを作成するためのいくつかのメソッド(Add、AddWithValue)がありますが、それらはすべて、不必要にパラメーター名を心配する必要があり、すべてコード内のSQLの可読性を低下させます。 SQLについて瞑想しようとしているとき、パラメーターでどの値が使用されているかを確認するために、上または下を探し回る必要があります。

私は、私の小さなSqlBuilderクラスがパラメーター化されたクエリを記述する最もエレガントな方法であると謙虚に主張しています。コードは次のようになります...

C#

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();

コードが短くなり、読みやすくなります。余分な行さえ必要ありません。また、読み返すときに、パラメーターの値を探し回る必要もありません。必要なクラスはこちらです...

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
    _rq = new StringBuilder();
    _cmd = cmd;
    _seq = 0;
}
public SqlBuilder Append(String str)
{
    _rq.Append(str);
    return this;
}
public SqlBuilder Value(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append(paramName);
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public SqlBuilder FuzzyValue(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append("'%' + " + paramName + " + '%'");
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public override string ToString()
{
    return _rq.ToString();
}
}
2
bbsimonbb

古い投稿ですが、新参者がストアドプロシージャを認識できるようにしたかった。

ここでの私の10¢の価値は、SQLステートメントをストアドプロシージャとして記述できる場合、私の見解では最適なアプローチであるということです。 I ALWAYSストアドプロシージャを使用し、メインコードのレコードをループしません。例:SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class

ストアドプロシージャを使用する場合、ユーザーをEXECUTE権限のみに制限することができます。したがって、セキュリティリスクの低減になります。

ストアドプロシージャは本質的にパラメータ化されており、入力パラメータと出力パラメータを指定できます。

ストアドプロシージャ(SELECTステートメントを介してデータを返す場合)は、コード内の通常のSELECTステートメントとまったく同じ方法でアクセスして読み取ることができます。

また、SQL Serverでコンパイルされるため、より高速に実行されます。

また、複数のステップを実行できることにも触れました。 updateテーブル。別のDBサーバーで値を確認し、最後に終了したら、すべて同じサーバー上のクライアントにデータを返します。クライアントとの対話はありません。そのため、このロジックをコードでコーディングするよりもはるかに高速です。

0
Arvin Amir