Как создать параметризованный 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. http://www.sommarskog.se/dynamic_sql.html
6 ответов:
ваш пример EXEC не будет параметризован. Вам нужны параметризованные запросы (подготовленные операторы в некоторых кругах), чтобы предотвратить такой ввод от причинения ущерба:
'; DROP TABLE bar;--
Попробуйте поместить это в переменную 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-инъекций. Однако в большинстве случаев вам все равно придется вызывать их с помощью параметров запроса, иначе они не помогут. Если вы используете хранимые процедуры исключительно, затем вы можете отключить разрешения для выбора, обновления, изменения, создания, удаления и т. д. (Почти все, кроме EXEC) для учетной записи пользователя приложения и получить некоторую защиту таким образом.
определенно последний, т. е.
или мне нужно сделать что-то более обширное ...? (Да,
cmd.Parameters.Add()
)параметризованные запросы имеют два основных преимущества:
- безопасность: это хороший способ избежать SQL Injection уязвимости
- производительность: если вы регулярно вызываете один и тот же запрос только с разными параметрами, параметризованный запрос может позволить базе данных кэшировать ваши запросы, которые являются значительный источник увеличения производительности.
- дополнительно: вам не придется беспокоиться о проблемах форматирования даты и времени в коде базы данных. Аналогично, если ваш код когда-либо будет работать на машинах с неанглийским языком, у вас не будет проблем с десятичными точками / десятичными запятыми.
вы хотите пойти с вашим последним примером, так как это единственный, который действительно параметризован. Помимо проблем безопасности (которые гораздо более распространены, чем вы могли бы подумать), лучше всего позволить ADO.NET обработайте параметризацию, поскольку вы не можете быть уверены, требует ли передаваемое значение одинарных кавычек вокруг него или нет, не проверяя
Type
каждого параметра.[Edit] вот пример:
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);
большинство людей будет делать это через библиотеку языков программирования на стороне сервера, например, PDO PHP или 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*/}
Это заботится о том, чтобы избежать ваших данных для вставки базы данных.
одним из преимуществ является то, что вы можете повторить вставку много раз с одним подготовленным оператором, получая преимущество в скорости.
например, в приведенном выше запросе я мог бы подготовить инструкцию один раз, а затем выполнить цикл создания данных массив из кучи данных и повторите ->выполнить столько раз, сколько необходимо.
текст вашей команды должен быть таким:
cmdText = "SELECT foo FROM bar WHERE baz = ?" cmdText = "EXEC foo_from_baz ?"
затем добавьте значения параметров. Этот способ гарантирует, что значение con будет использоваться только как значение, тогда как с другим методом, если переменная fuz установлена в
"x'; delete from foo where 'a' = 'a"
вы видите, что может произойти?
вот короткий класс, чтобы начать с 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