Почему мы всегда предпочитаем использовать параметры в операторах SQL? - PullRequest
102 голосов
/ 21 сентября 2011

Я очень плохо знаком с работой с базами данных.Теперь я могу писать команды SELECT, UPDATE, DELETE и INSERT.Но я видел много форумов, где мы предпочитаем писать:

SELECT empSalary from employee where salary = @salary

... вместо:

SELECT empSalary from employee where salary = txtSalary.Text

Почему мы всегда предпочитаем использовать параметры и как бы я их использовал?

Я хотел бы узнать об использовании и преимуществах первого метода.Я даже слышал об SQL-инъекциях, но не до конца понимаю.Я даже не знаю, связано ли внедрение SQL с моим вопросом.

Ответы [ 7 ]

114 голосов
/ 21 сентября 2011

Использование параметров помогает предотвратить Атаки SQL-инъекций , когда база данных используется вместе с программным интерфейсом, таким как программа для ПК или веб-сайт.

В вашем примере пользователь может напрямуюзапустить код SQL в вашей базе данных, создав операторы в txtSalary.

Например, если бы они написали 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:

Согласно комментарию Джорджа Стокера, я изменил пример кода, чтобы не использовать AddWithValue.Кроме того, обычно рекомендуется заключать IDisposable s в using операторов.

68 голосов
/ 21 сентября 2011

Вы правы, это связано с SQL-инъекцией , которая является уязвимостью, которая позволяет пользователю malicioius выполнять произвольные операторы для вашей базы данных. Этот старый фаворит XKCD комикс иллюстрирует концепцию:

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.

Когда вы выполните этот запрос, он выполнит SELECT и UPDATE или DROP, или что угодно. -- в конце просто комментирует остальную часть вашего запроса, что было бы полезно при атаке, если вы объединяете что-либо после txtSalary.Text.


Правильный способ - использовать параметризованные запросы, например (C #):

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

С этим вы можете безопасно выполнить запрос.

Чтобы узнать, как избежать SQL-инъекций на нескольких других языках, посетите веб-сайт bobby-tables.com , поддерживаемый SO пользователем .

5 голосов
/ 11 июня 2015

В дополнение к другим ответам нужно добавить, что параметры не только помогают предотвратить внедрение SQL, но может улучшить производительность запросов .Сервер Sql кэширует параметризованные планы запросов и повторно использует их при повторном выполнении запросов.Если вы не параметризовали свой запрос, то sql сервер будет компилировать новый план для каждого запроса (с некоторым исключением), если текст запроса будет отличаться.

Дополнительная информация о кэшировании плана запроса

3 голосов
/ 08 февраля 2017

Через два года после моего первого перехода , я рецидивирую ...

Почему мы предпочитаем параметры?Внедрение SQL, очевидно, является серьезной причиной, но может быть, мы тайно стремимся вернуться к SQL в качестве языка .SQL в строковых литералах уже является странной культурной практикой, но по крайней мере вы можете скопировать и вставить свой запрос в Management Studio.SQL, динамически создаваемый с помощью условных выражений и управляющих структур, когда SQL имеет условные обозначения и управляющие структуры, является варварством уровня 0.Вы должны запустить ваше приложение в режиме отладки или с трассировкой, чтобы увидеть, какой SQL он генерирует.

Не останавливайтесь только на параметрах.Пройдите весь путь и используйте QueryFirst (заявление об отказе: которое я написал).Ваш SQL живет в файле .sql .Вы редактируете его в невероятном окне редактора TSQL, с проверкой синтаксиса и Intellisense для ваших таблиц и столбцов.Вы можете назначить тестовые данные в специальном разделе комментариев и нажать «играть», чтобы выполнить свой запрос прямо в окне.Создать параметр так же просто, как вставить «@myParam» в ваш SQL.Затем при каждом сохранении QueryFirst создает оболочку C # для вашего запроса.Ваши параметры, строго типизированные, всплывают как аргументы методов Execute ().Ваши результаты возвращаются в IEnumerable или Список строго типизированных POCO, типов, сгенерированных из фактической схемы, возвращаемой вашим запросом.Если ваш запрос не выполняется, ваше приложение не будет скомпилировано.Если ваша схема БД изменяется и ваш запрос выполняется, но некоторые столбцы исчезают, ошибка компиляции указывает на строку в вашем коде , которая пытается получить доступ к отсутствующим данным.И есть множество других преимуществ. Зачем вам доступ к данным любым другим способом?

3 голосов
/ 22 сентября 2011

В Sql, когда любое слово содержит знак @, это означает, что оно является переменной, и мы используем эту переменную, чтобы установить значение в ней и использовать ее в числовой области в том же сценарии SQL, поскольку оно ограничено только одним сценарием, в то время как вы можете объявить много переменных одного типа и имени на многих скриптах. Мы используем эту переменную в партии хранимых процедур, потому что хранимая процедура - это предварительно скомпилированные запросы, и мы можем передавать значения в этой переменной из сценария, рабочего стола и веб-сайтов для получения дополнительной информации: Объявление локальной переменной , Хранимая процедура Sql и sql инъекции .

Также прочитайте Защита от SQL-инъекций , это поможет вам защитить вашу базу данных.

Надеюсь, это поможет вам понять и любой вопрос, прокомментируйте меня.

2 голосов
/ 05 декабря 2014

Другие ответы охватывают, почему параметры важны, но есть и обратная сторона!В .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();
}
}
0 голосов
/ 07 июня 2017

Старая публикация, но хотелось убедиться, что новички знают о Хранимых процедурах .

Моя ценность 10 is заключается в том, что если вы сможете написать свой оператор SQL как сохраненныйпроцедура , что на мой взгляд является оптимальным подходом.Я ВСЕГДА использую хранимые процедуры и никогда не перебираю записи в моем основном коде.Например: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.

Когда вы используете хранимые процедуры, вы можете ограничить пользователя только разрешениями EXECUTE , таким образом уменьшая риски безопасности .

Ваша хранимая процедура по сути парамеризована, и вы можете указать входные и выходные параметры.

Хранимая процедура (если она возвращает данные с помощью оператора SELECT) может быть доступна и прочитана точно так же, как выбудет регулярным SELECT оператором в вашем коде.

Он также работает быстрее, поскольку компилируется на SQL Server.

Я также упоминал, что вы можете сделать несколько шагов, например, update таблицу, проверить значения на другом сервере БД изатем, наконец, закончив, возвращайте данные клиенту, все на одном сервере, и не взаимодействуйте с клиентом.Так что это НАМНОГО быстрее, чем кодирование этой логики в вашем коде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...