Каковы хорошие способы предотвращения SQL-инъекций?

Мне нужно запрограммировать систему управления приложениями для моей компании OJT. Внешний интерфейс будет выполнен на C#, а серверный — на SQL.

Я никогда раньше не занимался проектом такого масштаба; в школе у ​​нас были только базовые уроки по SQL. Почему-то наш учитель совершенно не обсуждал SQL-инъекции, с чем я столкнулся только сейчас, прочитав об этом в сети.

Итак, в любом случае, мой вопрос: как вы предотвращаете SQL-инъекции в С#? Я смутно думаю, что это можно сделать, правильно замаскировав текстовые поля приложения, чтобы оно принимало ввод только в указанном формате. Например: текстовое поле электронной почты должно иметь формат «[email protected]». Будет ли этого подхода достаточно? Или в .NET есть предопределенные методы, которые обрабатывают такие вещи? Могу ли я применить фильтр к текстовому полю, чтобы он принимал только формат адреса электронной почты или текстовое поле имени, чтобы он не принимал специальные символы?


person LeonidasFett    schedule 17.01.2013    source источник
comment
другой вопрос был опубликован позже , но является более исчерпывающим.   -  person CodeCaster    schedule 03.02.2016


Ответы (4)


Используя SqlCommand и его дочерний набор параметров всю боль проверки SQL-инъекция забирается у вас и будет обрабатываться этими классами.

Вот пример, взятый из одной из статей выше:

private static void UpdateDemographics(Int32 customerID,
    string demoXml, string connectionString)
{
    // Update the demographics for a store, which is stored  
    // in an xml column.  
    string commandText = "UPDATE Sales.Store SET Demographics = @demographics "
        + "WHERE CustomerID = @ID;";

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        SqlCommand command = new SqlCommand(commandText, connection);
        command.Parameters.Add("@ID", SqlDbType.Int);
        command.Parameters["@ID"].Value = customerID;

        // Use AddWithValue to assign Demographics. 
        // SQL Server will implicitly convert strings into XML.
        command.Parameters.AddWithValue("@demographics", demoXml);

        try
        {
            connection.Open();
            Int32 rowsAffected = command.ExecuteNonQuery();
            Console.WriteLine("RowsAffected: {0}", rowsAffected);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
person Oliver    schedule 17.01.2013
comment
Что делать, если я хочу активно идентифицировать попытку SQL-инъекции. Существуют ли какие-либо хорошие библиотеки, которые можно использовать с этим подходом, чтобы я мог идентифицировать и зарегистрировать атаку? - person rolls; 06.12.2016
comment
Первый и самый простой подход к SQL-внедрению — это подход к завершению текущей строки и оператора, начиная значение с одинарной или двойной кавычки, за которой следуют фигурная скобка и точка с запятой. Таким образом, проверка данного ввода, если он начинается с них, была бы хорошей подсказкой, возможно, с помощью регулярного выражения, такого как ^\s*['"]\s*\)\s*;. Это не единственный способ, но самый распространенный. - person Oliver; 06.12.2016
comment
Обратите внимание, что SqlCommand также является IDisposable, поэтому должен находиться в блоке using. И вы можете прочитать можно ли нам прекратить использовать AddWithValue. - person Richardissimo; 04.01.2019
comment
Обратите внимание, что я реализовал этот код, и он рухнул, встретив «n» с «~» над ним. До того, как это произошло, это было похоже на чудо, теперь я не уверен насчет других языков... я надеялся, что это сделает их безопасными. - person karezza; 05.09.2019

SQL-инъекция может быть сложной проблемой, но есть способы ее обойти. Ваш риск снижается просто за счет использования ORM, такого как Linq2Entities, Linq2SQL, NHibrenate. Однако у вас могут возникнуть проблемы с SQL-инъекциями даже с ними.

Главное в SQL-инъекциях — это ввод, контролируемый пользователем (как и в XSS). В самом простом примере, если у вас есть форма входа (надеюсь, у вас никогда не будет такой, которая просто делает это), которая принимает имя пользователя и пароль.

SELECT * FROM Users WHERE Username = '" + username + "' AND password = '" + password + "'"

Если бы пользователь ввел следующее для имени пользователя Admin' --, оператор SQL выглядел бы так при выполнении в базе данных.

SELECT * FROM Users WHERE Username = 'Admin' --' AND password = ''

В этом простом случае использование параметризованного запроса (что делает ORM) устранит ваш риск. У вас также есть проблема с менее известным вектором атаки SQL-инъекцией, а именно с хранимыми процедурами. В этом случае, даже если вы используете параметризованный запрос или ORM, у вас все равно будет проблема с внедрением SQL. Хранимые процедуры могут содержать команды выполнения, и сами эти команды могут быть уязвимы для атак путем внедрения кода SQL.

CREATE PROCEDURE SP_GetLogin @username varchar(100), @password varchar(100) AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ' SELECT * FROM users' +
              ' FROM Product Where username = ''' + @username + ''' AND password = '''+@password+''''

EXECUTE sp_executesql @sql

Таким образом, в этом примере будет та же проблема с внедрением SQL, что и в предыдущем, даже если вы используете параметризованные запросы или ORM. И хотя пример кажется глупым, вы будете удивлены тем, как часто пишут что-то подобное.

Мои рекомендации заключаются в том, чтобы использовать ORM, чтобы немедленно снизить вероятность возникновения проблемы с SQL-инъекцией, а затем научиться определять код и хранимые процедуры, которые могут иметь проблему, и работать над ее устранением. Я не рекомендую использовать ADO.NET (SqlClient, SqlCommand и т. д.) напрямую, если вам это не нужно, не потому, что использовать его с параметрами как-то небезопасно, а потому, что гораздо проще облениться и просто начать писать SQL запрос с использованием строк и просто игнорируя параметры. ORMS прекрасно заставляют вас использовать параметры, потому что именно это они и делают.

Далее Посетите сайт OWASP по внедрению SQL https://www.owasp.org/index.php/SQL_Injection и используйте шпаргалка по SQL-инъекциям, чтобы убедиться, что вы можете обнаружить и устранить любые проблемы, которые возникнут в вашем коде. https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet, наконец, я бы сказал внедрите хороший обзор кода между вами и другими разработчиками в вашей компании, где вы можете просматривать код друг друга на предмет таких вещей, как внедрение SQL и XSS. Очень часто программисты пропускают этот материал, потому что пытаются поторопиться с какой-то функцией и не тратят слишком много времени на просмотр своего кода.

person nerdybeardo    schedule 17.01.2013
comment
Я не знаю о каких-либо рисках SQL-инъекций с EF/Similar ORM, поскольку они используют внутренние параметризованные запросы. Можете ли вы предоставить ссылку? Спасибо - person Basic; 17.01.2013
comment
Пример, который я привел выше с хранимой процедурой, по-прежнему будет подвержен атаке с внедрением SQL, даже если вы используете ORM, например Entity Framework, для вызова процедуры, потому что уязвимость находится в самой процедуре. Поэтому я пытался донести, что вы не можете просто использовать ORM и думать, что вы покрыли 100% случаев атак SQL-инъекций. Вот ссылка для получения дополнительной информации: troyhunt.com /2012/12/stored-procedures-and-orms-wont-save.html - person nerdybeardo; 17.01.2013
comment
Если вы используете ORM, зачем вам использовать sproc? Но да, я понимаю вашу точку зрения, что если вы хотите сделать что-то вручную, вы можете ввести уязвимости. - person Basic; 17.01.2013
comment
Хотя я согласен с тем, что в последнее время использование хранимых процедур сократилось из-за появления ORM, все же есть много деловых ситуаций, которые требуют их использования. Если без уважительной причины, возможно, у вас есть архитектор данных, который требует их использования для определенных процессов или соображений производительности. Я не знаю многих баз данных, в которых даже сегодня нет хранимой процедуры, поэтому уменьшение их значения в SDLC может привести к ошибке SQL-инъекции в приложении. Мы, разработчики, иногда живем в мире того, что хотим, а не в реальности бизнеса. - person nerdybeardo; 17.01.2013

Мой ответ довольно прост:

Используйте Entity Framework для связи между C# и вашей базой данных SQL. Это сделает параметризованные строки SQL, которые не уязвимы для SQL-инъекций.

Как бонус, с ним очень легко работать.

person Øyvind Bråthen    schedule 17.01.2013
comment
Однако это не всегда вариант, когда вы не знаете, как будут выглядеть возвращаемые данные. - person saluce; 18.12.2015
comment
Я не люблю ЭФ. Я предпочитаю легкие ORMS, такие как Dapper и Insight.Database. При использовании ORM вы можете увидеть/записать точный запрос, который необходимо выполнить. - person Rez.Net; 22.07.2019
comment
Как минус, это добавляет много накладных расходов на производительность. - person Liam; 10.12.2020

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

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

Вам следует изучить этот вопрос подробнее после того, как вы точно решили, какой будет ваша интерфейсная технология.

person Jon    schedule 17.01.2013
comment
извините, но что вы подразумеваете под написанием голого sql? - person LeonidasFett; 17.01.2013
comment
@LeonidasFett: Обычно вы работаете с базой данных, написав код типа User.Name = "Joe"; User.Save();, а не код типа Database.ExecuteQuery("UPDATE users SET name = 'Joe' WHERE id = 1");. Второй пишет голый sql. - person Jon; 17.01.2013
comment
Ах хорошо. теперь я понимаю :) так что в основном я должен взаимодействовать с базой данных через фреймворк, а не sql? - person LeonidasFett; 17.01.2013
comment
То, что вы здесь защищаете, - это ORM - как структура сущности. Я использую его сам, и мне это нравится, но у него есть некоторые недостатки, например, он очень медленный для массовых операций. Альтернативой является использование параметризованных команд: вы пишете SQL с заполнителями для ваших переменных, а затем передаете переменные другому методу, который позаботится об их слиянии с запросом для вас (на самом деле они передаются отдельно в базу данных). сервер, который обрабатывает все остальное). Дополнительную информацию см. здесь - person Basic; 17.01.2013
comment
@LeonidasFett: Как правило, да. Если вам нужно написать чистый SQL в определенных местах, фреймворк предоставит способ сделать это. - person Jon; 17.01.2013
comment
@Basic: избегать как можно больше не означает никогда не делать этого под страхом смерти. Это эмпирическое правило, сформулированное таким образом, что оно предназначено для тех, кто абсолютно ничего не признал в лучшем случае очень малом предыдущем опыте. - person Jon; 17.01.2013
comment
@Джон, я не был с вами не согласен, просто указал, что прежде чем вы пойдете и построите огромную систему на основе EF, вы должны знать, что это не волшебная пуля. Я определенно думаю, что это отличное решение для большинства сценариев, когда вы не манипулируете десятками тысяч записей. - person Basic; 17.01.2013
comment
На данный момент в существующей базе данных около 3000 записей, но в течение этого года она будет значительно расширена. я перечислю два варианта (EF и параметризованные команды) с преимуществами и недостатками каждого, и пусть мой руководитель проекта решит: D - person LeonidasFett; 17.01.2013