Nasıl parametreli bir SQL sorgusu oluşturabilirim? Neden yapayım?


94

"Herkesin", her kullanıcı girdisini doğrulamak zorunda kalmadan SQL enjeksiyon saldırılarına karşı koruma sağlamak için parametreli SQL sorguları kullandığını duydum.

Bunu nasıl yapıyorsun? Depolanan prosedürleri kullanırken bunu otomatik olarak alıyor musunuz?

Anladığım kadarıyla bu parametreleştirilmemiş:

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

Bu parametrelendirilebilir mi?

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

Yoksa kendimi SQL enjeksiyonundan korumak için bunun gibi daha kapsamlı bir şey mi yapmalıyım?

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

Güvenlik hususları dışında parametreli sorguları kullanmanın başka avantajları var mı?

Güncelleme: Bu harika makale, Grotok'un soru referanslarından birine bağlandı. http://www.sommarskog.se/dynamic_sql.html


Görünüşe göre bu sorunun daha önce Stackoverflow'da sorulmamış olmasını şok edici buldum. Çok iyiydi!
Tamas Czinege

3
Oh, var. Elbette çok farklı bir şekilde ifade edildi, ama oldu.
Joel Coehoorn

10
Little Bobby Tables'ın verilerinizi yok etmesini önlemek için parametrize sorgu kullanmalısınız .
Direnemedi

4
With bloğunun nesi bu kadar kötü?
Lurker Indeed

1
"With bloğunun nesi bu kadar kötü" sorusu için # sorusu olan var mı?
Jim

Yanıtlar:


77

EXEC örneğiniz parametreleştirilmez. Bunun gibi girdilerin hasara neden olmasını önlemek için parametreli sorgulara (bazı çevrelerde hazırlanmış ifadeler) ihtiyacınız var:

'; DROP TABLE çubuğu; -

Bunu fuz değişkeninize koymayı deneyin (veya bar tablonuza değer veriyorsanız etmeyin). Daha ince ve zarar verici sorgular da mümkündür.

Sql Server ile parametreleri nasıl yaptığınızla ilgili bir örnek:

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

Saklanan prosedürler bazen SQL enjeksiyonunun engellenmesiyle ilişkilendirilir. Ancak, çoğu zaman onları sorgu parametrelerini kullanarak çağırmanız gerekir, aksi halde yardımcı olmazlar. Saklı yordamlar kullanırsanız münhasıran , o zaman uygulama kullanıcı hesabı için (hemen her fakat EXEC hakkında) vb DELETE, CREATE, SELECT'teki UPDATE, ALTER izinlerini kapatın ve bazı koruma yolu olduğunu alabilirsiniz.


cmd.Parameters.Add("@Baz", SqlDbType.VarChar, 50).Value = BazLütfen bunu biraz daha açıklar mısınız?
Cary Bondoc

1
@CaryBondoc, ne bilmek istiyorsun? Bu satır , dizenin değeri atanan @Baztürde bir parametre oluşturur . varchar(50)Baz
JB King

"command.parameters.addiwthvalue (" @ Baz ", 50)" da diyebilirsiniz
Gavin Perkins

2
@GavinPerkins Bunu kastettiğinizi varsayarsak AddWithValue("@Baz", Baz), bunu yapabilirsiniz , ancak yapmamalısınız , çünkü özellikle varsayılan olarak eşlenen dize değerlerini nvarchargerçek varchartüre dönüştürmek, bu bağlantıda belirtilen etkileri tetikleyebilecek en yaygın yerlerden biridir.
Joel Coehoorn

15

Kesinlikle sonuncusu, yani

Yoksa daha kapsamlı bir şey yapmam mı gerekiyor? (Evet, cmd.Parameters.Add())

Parametreleştirilmiş sorguların iki ana avantajı vardır:

  • Güvenlik: SQL Injection güvenlik açıklarından kaçınmanın iyi bir yoludur
  • Performans: Aynı sorguyu yalnızca farklı parametrelerle düzenli olarak çalıştırırsanız, parametreleştirilmiş bir sorgu, veritabanının önemli bir performans kazanımı kaynağı olan sorgularınızı önbelleğe almasına izin verebilir.
  • Ekstra: Veritabanı kodunuzdaki tarih ve saat biçimlendirme sorunları hakkında endişelenmenize gerek kalmayacak. Benzer şekilde, kodunuz İngilizce olmayan bir yerel ayarı olan makinelerde çalışacaksa, ondalık basamaklar / ondalık virgüllerle sorun yaşamazsınız.

5

Son örneğinizle gitmek istiyorsunuz, çünkü bu gerçekten parametreleştirilmiş tek örnek. Güvenlik kaygılarının yanı sıra (ki bunlar düşündüğünüzden çok daha yaygındır), ADO.NET'in parametrelendirmeyi işlemesine izin vermek en iyisidir, çünkü aktardığınız değerin etrafında tek tırnak işareti gerektirip gerektirmediğinden emin olamazsınız Type, her bir parametreyi incelemeden. .

[Düzenle] İşte bir örnek:

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
Şuna dikkat edin: .Net dizeleri unicode olduğundan parametre varsayılan olarak NVarChar'ı varsayacaktır. Gerçekten bir VarChar sütunu ise, bu büyük performans sorunlarına neden olabilir.
Joel Coehoorn

2

Çoğu insan bunu PHP'nin PDO'su veya Perl DBI gibi bir sunucu tarafı programlama dili kitaplığı aracılığıyla yapar.

Örneğin, PDO'da:

$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*/}

Bu, veritabanının eklenmesi için verilerinizden kaçış yapılmasını sağlar.

Bir avantajı, bir eki hazırlanmış bir ifade ile birçok kez tekrarlayabilmeniz ve hız avantajı elde etmenizdir.

Örneğin, yukarıdaki sorguda ifadeyi bir kez hazırlayabilir ve ardından bir grup veriden veri dizisi oluşturmanın üzerinden geçebilir ve -> çalıştırmayı gerektiği kadar tekrar edebilirim.


1

Komut metniniz şöyle olmalıdır:

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

cmdText = "EXEC foo_from_baz ?"

Ardından parametre değerlerini ekleyin. Bu yol, değerin yalnızca bir değer olarak kullanılmasını sağlarken, diğer yöntemde değişken fuz olarak

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

neler olabileceğini görebiliyor musun?


0

İşte SQL ile başlamak için kısa bir sınıf ve buradan derleyip sınıfa ekleyebilirsiniz.

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 / Ekspres

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.