параметризованные запросы против SQL-инъекций

Я новичок в Asp.net, и я только начинаю работать с classами. Недавно я создал class, который будет обрабатывать большинство моих SQL-запросов для меня, чтобы мне не приходилось многократно создавать новые подключения по всем моим файлам.

Один из методов, которые я создал, принимает SQL-запрос в качестве параметра и возвращает результат. Я знаю, что я должен использовать параметризованные запросы, чтобы избежать инъекций SQL. Мой вопрос в том, как я могу это сделать, когда передаю запрос как строковый параметр?

Например, вот метод, который я буду называть:

public static DataTable SqlDataTable(string sql) { using (SqlConnection conn = new SqlConnection(DatabaseConnectionString)) { SqlCommand cmd = new SqlCommand(sql, conn); cmd.Connection.Open(); DataTable TempTable = new DataTable(); TempTable.Load(cmd.ExecuteReader()); return TempTable; } } 

Поэтому из другого файла я бы хотел использовать этот метод следующим образом:

 DataTable dt = new DataTable(); dt = SqlComm.SqlDataTable("SELECT * FROM Users WHERE UserName='" + login.Text + "' and Password='" + password.Text + "'"); if (dt.Rows.Count > 0) { // do something if the query returns rows } 

Это работает, но все равно будет уязвимым для инъекций? Есть ли способ передать переменные в строку в качестве параметров? Я знаю, что могу сделать это, если я создам новый объект SQLCommand для запроса и использую Parameters.AddWithValue, но я хотел, чтобы все мои команды SQL были в отдельном classе.

Это работает, но все равно будет уязвимым для инъекций?

Да, ваш код ужасно уязвим для SQL-инъекций.

Я знаю, что я должен использовать параметризованные запросы, чтобы избежать инъекций SQL.

Ах, да.

Мой вопрос в том, как я могу это сделать, когда передаю запрос как строковый параметр?

Вы просто не должны передавать запрос как строковый параметр. Вместо этого вы должны передавать запрос как строковый параметр, содержащий заполнители и значения для этих заполнителей:

 public static DataTable SqlDataTable(string sql, IDictionary values) { using (SqlConnection conn = new SqlConnection(DatabaseConnectionString)) using (SqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = sql; foreach (KeyValuePair item in values) { cmd.Parameters.AddWithValue("@" + item.Key, item.Value); } DataTable table = new DataTable(); using (var reader = cmd.ExecuteReader()) { table.Load(reader); return table; } } } 

а затем используйте свою функцию следующим образом:

 DataTable dt = SqlComm.SqlDataTable( "SELECT * FROM Users WHERE UserName = @UserName AND Password = @Password", new Dictionary { { "UserName", login.Text }, { "Password", password.Text }, } ); if (dt.Rows.Count > 0) { // do something if the query returns rows } 

То, что вы пытаетесь сделать, имеет прекрасный логический смысл, и я могу понять, почему вы пришли к этой реализации. Однако то, что вы пытаетесь сделать, очень опасно и, будучи новичком в ASP.NET, возможно, вам не известно, что есть множество других доступных вам вариантов, которые упрощают и значительно упрощают управление вашими данными.

@iamkrillin намекнул на одну из таких технологий – Object Relational Mapping (ORM). У платформы .NET действительно есть поддержка первого classа для ORM, называемого Entity Framework . Я считаю, что причина, по которой он предположил, что вы заглядываете в ORM, заключается в том, что ваш дизайн на самом деле очень похож на принцип работы ORM. Они являются абстрагированными classами, которые представляют таблицы в вашей базе данных, которые можно легко запросить с помощью LINQ. Запросы LINQ автоматически параметризуются и избавляют вас от стресса в управлении безопасностью ваших запросов. Они генерируют SQL «на лету» (так же, как вы передаете строки вашему classу доступа к данным), и намного более гибки в том, как они могут возвращать данные (массивы, списки, вы называете это).

Однако один из недостатков ORM заключается в том, что они имеют довольно крутые кривые обучения. Более простой вариант (хотя и немного старше EF) заключается в использовании Typed Datasets. Типизированные наборы данных намного проще создавать, чем создавать ORM и, как правило, намного проще реализовать. Хотя они не так гибки, как ORM, они выполняют именно то, что вы пытаетесь сделать в простой, безопасной и уже решенной манере. К счастью, когда ASP.NET впервые выпустила обучающие видеоролики, ориентированные в основном на типизированные наборы данных, и как таковые, есть множество высококачественных свободно доступных видео / учебников, которые помогут вам быстро и быстро.

Вы на правильном пути, и я действительно сделал то, что вы тоже ищете. Однако вместо того, чтобы просто передавать строку в вашу функцию, я передаю объект SQL Command … Таким образом, вы можете правильно построить все свои команды и параметры, а затем сказать … здесь, запустите это, это готов к работе. Что-то вроде

 public static DataTable SqlDataTable(SqlCommand cmd) { using (SqlConnection conn = new SqlConnection(DatabaseConnectionString)) { cmd.Connection = conn; // store your connection to the command object.. cmd.Connection.Open(); DataTable TempTable = new DataTable(); TempTable.Load(cmd.ExecuteReader()); return TempTable; } } public DataTable GetMyCustomers(string likeName) { SqlCommand cmd = new SqlCommand(); cmd.CommandText = "select * from SomeTable where LastName like "@someParm%"; cmd.Parameters.Add( "whateverParm", likeName ); // don't have SQL with me now, guessing syntax // so now your SQL Command is all built with parameters and ready to go. return SqlDataTable( cmd ); } 

Мое предложение: используйте орму. Есть много вариантов отныне дней

  • «Правильный» способ хранения двоичных данных с помощью C ++ / STL
  • Существует ли тип C # для представления целочисленного диапазона?
  • как я могу извлечь мантису двойного
  • Generics в C #, используя тип переменной как параметр
  • cpp - valgrind - Недопустимое чтение размером 8
  • Почему мы не можем передавать массивы по значению?
  • Как разобрать csv с помощью boost :: spirit
  • Когда и зачем мне нужно использовать cin.ignore () в C ++?
  • Может ли современное оборудование x86 не хранить один байт в памяти?
  • Правильный способ разбить std :: string на вектор
  • Получение выбранного значения combobox
  • Давайте будем гением компьютера.