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


93

「誰もが」パラメータ化された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


どうやらこの質問がStackoverflowで以前に尋ねられたことがないのは衝撃的でした。とても良いものです!
Tamas Czinege、2009

3
ああ、そうだ。もちろん、まったく違う言い方をしますが、そうです。
Joel Coehoorn、2009

9
Little Bobby Tablesがデータを破壊しないように、パラメーター化されたクエリを使用する必要があります。抵抗できませんでした:)
zendar

4
Withブロックの何が悪いのですか?
ラーカー、確かに

1
「Withブロックの何が悪いのか」という質問に対する質問がありますか?
ジムは

回答:


76

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インジェクションの防止が認められる場合があります。ただし、ほとんどの場合、クエリパラメータを使用してそれらを呼び出す必要があります。そうしないと役に立ちません。ストアドプロシージャを排他的に使用する場合は、アプリケーションユーザーアカウントに対するSELECT、UPDATE、ALTER、CREATE、DELETEなど(EXEC以外のほとんどすべて)のアクセス許可をオフにして、そのように保護することができます。


これcmd.Parameters.Add("@Baz", SqlDbType.VarChar, 50).Value = Bazについて詳しく説明していただけますか?
Cary Bondoc、2015

1
@CaryBondoc、何が知りたいですか?その行は、文字列の値が割り当てられる@Bazタイプのパラメーターを作成します。varchar(50)Baz
JBキング

「command.parameters.addiwthvalue( "@ Baz"、50)」と言うこともできます
Gavin Perkins

2
@GavinPerkinsを意図していたAddWithValue("@Baz", Baz)としても可能ですが、そうするべきではありません。特に、デフォルトでnvarchar実際にマッピングされる文字列値を実際のvarchar型に変換することは、そのリンクで言及されている効果をトリガーできる最も一般的な場所の1つであるためです。
Joel Coehoorn

15

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

それとも、もっと広範囲なことをする必要がありますか...?(はい、cmd.Parameters.Add()

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

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

5

これは本当にパラメーター化されている唯一の例なので、最後の例を使用してください。セキュリティの懸念(考えられるよりもはるかに一般的です)に加えて、渡した値が単一引用符で囲む必要があるかどうか、またはType各パラメーターの検査なしでは確認できないため、ADO.NETでパラメーター化を処理することをお勧めします。 。

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

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);

3
これに注意してください:.Net文字列はユニコードなので、パラメーターはデフォルトでNVarCharを想定します。それが本当にVarChar列である場合、これは大きなパフォーマンスの問題を引き起こす可能性があります。
Joel Coehoorn、2009

2

ほとんどの人は、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を必要なだけ繰り返します。


1

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

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

cmdText = "EXEC foo_from_baz ?"

次に、パラメーター値を追加します。このようにして、値conが値として使用されるだけになるのを確実にしますが、変数fuzが

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

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


0

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.