web-dev-qa-db-ja.com

パラメータ化されたSQLクエリを作成するにはどうすればよいですか?どして私がこんな事に?

「全員」がパラメーター化されたSQLクエリを使用して、すべてのユーザー入力を検証せずにSQLインジェクション攻撃から保護していると聞きました。

これどうやってやるの?ストアドプロシージャを使用するときにこれを自動的に取得しますか?

だから私の理解はこれはパラメータ化されていません:

cmdText = String.Format("SELECT foo FROM bar WHERE baz = '{0}'", fuz)

これはパラメーター化されますか?

cmdText = String.Format("EXEC foo_from_baz '{0}'", fuz)

または、SQLインジェクションから自分自身を保護するために、このようなより広範なことを行う必要がありますか?

With command
    .Parameters.Count = 1
    .Parameters.Item(0).ParameterName = "@baz"
    .Parameters.Item(0).Value = fuz
End With

セキュリティに関する考慮事項に加えて、パラメーター化されたクエリを使用することには他の利点がありますか?

更新:この素晴らしい記事は、Grotokによる質問のリファレンスの1つにリンクされていました。 http://www.sommarskog.se/dynamic_sql.html

92
Jim Counts

EXECの例はパラメーター化されません。このような入力による損傷を防ぐには、パラメーター化されたクエリ(一部の円で準備されたステートメント)が必要です。

';ドロップテーブルバー;-

それをfuz変数に入れてみてください(バーテーブルを評価する場合は、入れないでください)。より微妙で有害なクエリも可能です。

以下は、SQL Serverでパラメーターを実行する方法の例です。

Public Function GetBarFooByBaz(ByVal Baz As String) As String
    Dim sql As String = "SELECT foo FROM bar WHERE baz= @Baz"

    Using cn As New SqlConnection("Your connection string here"), _
        cmd As New SqlCommand(sql, cn)

        cmd.Parameters.Add("@Baz", SqlDbType.VarChar, 50).Value = Baz
        Return cmd.ExecuteScalar().ToString()
    End Using
End Function

ストアドプロシージャは、SQLインジェクションの防止に貢献している場合があります。ただし、ほとんどの場合、クエリパラメータを使用して呼び出す必要があります。そうしないと解決できません。ストアドプロシージャexclusivelyを使用する場合、アプリケーションユーザーアカウントのSELECT、UPDATE、ALTER、CREATE、DELETEなどのアクセス許可(EXEC以外のほとんどすべて)をオフにして、そのような保護を取得できます。 。

75
Joel Coehoorn

間違いなく最後のもの、すなわち.

または、もっと大規模に行う必要がありますか...? (はい、cmd.Parameters.Add()

パラメータ化されたクエリには、主に2つの利点があります。

  • セキュリティ: SQLインジェクション 脆弱性を回避する良い方法です
  • パフォーマンス:異なるパラメーターを使用して同じクエリを定期的に呼び出すと、パラメーター化されたクエリによってデータベースがクエリをキャッシュできるため、パフォーマンスが大幅に向上します。
  • 追加:データベースコードの日付と時刻のフォーマットの問題を心配する必要はありません。同様に、英語以外のロケールのマシンでコードを実行する場合、小数点/小数点のコンマに問題はありません。
15
Tamas Czinege

これは本当にパラメーター化されている唯一の例なので、最後の例を使用します。セキュリティ上の懸念(考えられるよりもはるかに一般的です)に加えて、渡される値がType

[編集]以下に例を示します。

SqlCommand command = new SqlCommand(
    "select foo from bar where baz = @baz",
    yourSqlConnection
);

SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@baz";
parameter.Value = "xyz";

command.Parameters.Add(parameter);
5
Andrew Hare

ほとんどの人は、PHPのPDOやPerl DBIなどのサーバー側プログラミング言語ライブラリを介してこれを行います。

たとえば、PDOの場合:

$dbh=pdo_connect(); //you need a connection function, returns a pdo db connection

$sql='insert into squip values(null,?,?)';

$statement=$dbh->prepare($sql);

$data=array('my user supplied data','more stuff');

$statement->execute($data);

if($statement->rowCount()==1){/*it worked*/}

これにより、データベースの挿入のためにデータがエスケープされます。

1つの利点は、1つの準備されたステートメントで挿入を何度も繰り返すことができ、速度の利点が得られることです。

たとえば、上記のクエリでは、ステートメントを1回準備してから、一連のデータからデータ配列を作成してループし、必要な回数だけ-> executeを繰り返します。

2
JAL

コマンドテキストは次のようにする必要があります。

cmdText = "SELECT foo FROM bar WHERE baz = ?"

cmdText = "EXEC foo_from_baz ?"

次に、パラメーター値を追加します。この方法では、値conが値としてのみ使用されることになりますが、他の方法では、変数fuzが

"x'; delete from foo where 'a' = 'a"

何が起こるかわかりますか?

1
Tony Andrews

以下は、SQLで開始する短いクラスです。そこからビルドして、クラスに追加できます。

MySQL

Public Class mysql

    'Connection string for mysql
    Public SQLSource As String = "Server=123.456.789.123;userid=someuser;password=somesecurepassword;database=somedefaultdatabase;"

    'database connection classes

    Private DBcon As New MySqlConnection
    Private SQLcmd As MySqlCommand
    Public DBDA As New MySqlDataAdapter
    Public DBDT As New DataTable
    Public BindSource As New BindingSource
    ' parameters
    Public Params As New List(Of MySqlParameter)

    ' some stats
    Public RecordCount As Integer
    Public Exception As String

    Function ExecScalar(SQLQuery As String) As Long
        Dim theID As Long
        DBcon.ConnectionString = SQLSource
        Try
            DBcon.Open()
            SQLcmd = New MySqlCommand(SQLQuery, DBcon)
            'loads params into the query
            Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))

            'or like this is also good
            'For Each p As MySqlParameter In Params
            ' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
            ' Next
            ' clears params
            Params.Clear()
            'return the Id of the last insert or result of other query
            theID = Convert.ToInt32(SQLcmd.ExecuteScalar())
            DBcon.Close()

        Catch ex As MySqlException
            Exception = ex.Message
            theID = -1
        Finally
            DBcon.Dispose()
        End Try
        ExecScalar = theID
    End Function

    Sub ExecQuery(SQLQuery As String)

        DBcon.ConnectionString = SQLSource
        Try
            DBcon.Open()
            SQLcmd = New MySqlCommand(SQLQuery, DBcon)
            'loads params into the query
            Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))

            'or like this is also good
            'For Each p As MySqlParameter In Params
            ' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
            ' Next
            ' clears params

            Params.Clear()
            DBDA.SelectCommand = SQLcmd
            DBDA.Update(DBDT)
            DBDA.Fill(DBDT)
            BindSource.DataSource = DBDT  ' DBDT will contain your database table with your records
            DBcon.Close()
        Catch ex As MySqlException
            Exception = ex.Message
        Finally
            DBcon.Dispose()
        End Try
    End Sub
    ' add parameters to the list
    Public Sub AddParam(Name As String, Value As Object)
        Dim NewParam As New MySqlParameter(Name, Value)
        Params.Add(NewParam)
    End Sub
End Class

MS SQL/Express

Public Class MSSQLDB
    ' CREATE YOUR DB CONNECTION
    'Change the datasource
    Public SQLSource As String = "Data Source=someserver\sqlexpress;Integrated Security=True"
    Private DBCon As New SqlConnection(SQLSource)

    ' PREPARE DB COMMAND
    Private DBCmd As SqlCommand

    ' DB DATA
    Public DBDA As SqlDataAdapter
    Public DBDT As DataTable

    ' QUERY PARAMETERS
    Public Params As New List(Of SqlParameter)

    ' QUERY STATISTICS
    Public RecordCount As Integer
    Public Exception As String

    Public Sub ExecQuery(Query As String, Optional ByVal RunScalar As Boolean = False, Optional ByRef NewID As Long = -1)
        ' RESET QUERY STATS
        RecordCount = 0
        Exception = ""
        Dim RunScalar As Boolean = False

        Try
            ' OPEN A CONNECTION
            DBCon.Open()

            ' CREATE DB COMMAND
            DBCmd = New SqlCommand(Query, DBCon)

            ' LOAD PARAMS INTO DB COMMAND
            Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))

            ' CLEAR PARAMS LIST
            Params.Clear()

            ' EXECUTE COMMAND & FILL DATATABLE
            If RunScalar = True Then
                NewID = DBCmd.ExecuteScalar()
            End If
            DBDT = New DataTable
            DBDA = New SqlDataAdapter(DBCmd)
            RecordCount = DBDA.Fill(DBDT)
        Catch ex As Exception
            Exception = ex.Message
        End Try


        ' CLOSE YOUR CONNECTION
        If DBCon.State = ConnectionState.Open Then DBCon.Close()
    End Sub

    ' INCLUDE QUERY & COMMAND PARAMETERS
    Public Sub AddParam(Name As String, Value As Object)
        Dim NewParam As New SqlParameter(Name, Value)
        Params.Add(NewParam)
    End Sub
End Class
0
Chillzy