У нас здесь еще одна дискуссия об использовании параметризованных запросов sql в нашем коде. У нас есть две стороны в обсуждении: я и некоторые другие, которые говорят, что мы всегда должны использовать параметры для защиты от инъекций sql, и другие ребята, которые не думают, что это необходимо. Вместо этого они хотят заменить отдельные апострофы двумя апострофами во всех строках, чтобы избежать инъекций sql. Все наши базы данных работают под управлением Sql Server 2005 или 2008, а наша кодовая база работает на .NET framework 2.0.
Приведу простой пример на C #:
Я хочу, чтобы мы использовали это:
string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe
Пока другие ребята хотят это сделать:
string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?
Где функция SafeDBString определяется следующим образом:
string SafeDBString(string inputValue)
{
return "'" + inputValue.Replace("'", "''") + "'";
}
Теперь, пока мы используем SafeDBString для всех строковых значений в наших запросах, мы должны быть в безопасности. Правильно?
Есть две причины использовать функцию SafeDBString. Во-первых, так это делалось с каменных веков, а во-вторых, проще отлаживать операторы sql, поскольку вы видите точный запрос, выполняемый в базе данных.
Итак. Мой вопрос в том, действительно ли достаточно использовать функцию SafeDBString, чтобы избежать атак с использованием sql-инъекций. Я пытался найти примеры кода, который нарушает эту меру безопасности, но не могу найти никаких примеров.
Есть ли кто-нибудь, кто может это сломать? Как бы ты это сделал?
РЕДАКТИРОВАТЬ: Подводя итоги ответов на данный момент:
- Еще никто не нашел способа обойти SafeDBString на Sql Server 2005 или 2008. Думаю, это хорошо?
- В нескольких ответах отмечалось, что вы получаете прирост производительности при использовании параметризованных запросов. Причина в том, что планы запроса можно использовать повторно.
- Мы также согласны с тем, что использование параметризованных запросов дает более читаемый код, который легче поддерживать.
- Кроме того, проще всегда использовать параметры, чем использовать различные версии SafeDBString, преобразования строки в числа и преобразования строки в дату.
- Используя параметры, вы получаете автоматическое преобразование типов, что особенно полезно, когда мы работаем с датами или десятичными числами.
- И наконец: не пытайтесь самостоятельно обеспечивать безопасность, как писал JulianR. Поставщики баз данных тратят много времени и денег на безопасность. Нет способа сделать что-то лучше и нет причин, по которым мы должны пытаться делать их работу.
Так что, хотя никто не смог взломать простую безопасность функции SafeDBString, у меня есть много других хороших аргументов. Спасибо!
источник
Ответы:
Думаю, правильный ответ:
Не пытайтесь обеспечить безопасность самостоятельно . Используйте любую доступную надежную отраслевую стандартную библиотеку для того, что вы пытаетесь сделать, вместо того, чтобы пытаться сделать это самостоятельно. Какие бы предположения вы ни делали о безопасности, они могут быть неверными. Каким бы безопасным ни казался ваш собственный подход (и в лучшем случае он выглядит шатким), есть риск, что вы что-то упускаете, и действительно ли вы хотите воспользоваться этим шансом, когда дело касается безопасности?
Используйте параметры.
источник
И тогда кто-то идет и использует "вместо". Параметры, ИМО, единственный безопасный путь.
Это также позволяет избежать множества проблем с датами / числами; какая дата 01/02/03? Сколько будет 123456? Согласованы ли ваши серверы (app-server и db-server) друг с другом?
Если фактор риска их не убеждает, как насчет производительности? РСУБД может повторно использовать план запроса, если вы используете параметры, что способствует повышению производительности. Этого нельзя сделать только со строкой.
источник
Аргумент безвыходный. Если вам все же удастся найти уязвимость, ваши сотрудники просто изменят функцию SafeDBString, чтобы учесть ее, а затем снова попросят вас доказать, что это небезопасно.
Учитывая, что параметризованные запросами являются неоспоримым программированием передовой практики, бремя доказывания должно быть на них государство, почему они не используют метод, который одновременно является более безопасным и более эффективным.
Если проблема заключается в переписывании всего унаследованного кода, простым компромиссом будет использование параметризованных запросов во всем новом коде и рефакторинг старого кода, чтобы использовать их при работе с этим кодом.
Я предполагаю, что на самом деле проблема заключается в гордости и упрямстве, и с этим ничего не поделаешь.
источник
Во-первых, ваш образец для версии «Заменить» неверен. Вам нужно поставить апострофы вокруг текста:
Параметры делают для вас еще одно: вам не нужно беспокоиться о том, нужно ли заключать значение в кавычки. Конечно, вы можете встроить это в функцию, но тогда вам нужно добавить к функции много сложностей: как узнать разницу между NULL как null и NULL как просто строкой или между числом и строка, которая просто содержит много цифр. Это просто еще один источник ошибок.
Другое дело - производительность: параметризованные планы запросов часто кэшируются лучше, чем составные планы, что, возможно, экономит серверу шаг при выполнении запроса.
Кроме того, экранирования одинарных кавычек недостаточно. Многие продукты БД позволяют использовать альтернативные методы экранирования символов, которыми может воспользоваться злоумышленник. В MySQL, например, вы также можете экранировать одинарную кавычку с помощью обратной косой черты. Таким образом, следующее значение «name» взорвет MySQL только с помощью
SafeDBString()
функции, потому что, когда вы удваиваете одинарную кавычку, первая все еще экранируется обратной косой чертой, а вторая остается «активной»:Кроме того, JulianR поднимает хороший момент ниже: НИКОГДА не пытайтесь самостоятельно выполнять работу по обеспечению безопасности. Очень легко ошибиться в программировании системы безопасности незаметными способами, которые кажутся работающими даже после тщательного тестирования. Затем проходит время, и год спустя вы обнаруживаете, что ваша система была взломана шесть месяцев назад, и вы даже не знали об этом до тех пор.
Всегда максимально полагайтесь на библиотеки безопасности, предоставленные для вашей платформы. Они будут написаны людьми, которые зарабатывают на жизнь кодом безопасности, гораздо лучше протестированы, чем то, что вы можете сделать, и обслуживаются поставщиком в случае обнаружения уязвимости.
источник
Я бы сказал:
1) Почему вы пытаетесь заново реализовать что-то встроенное? он там, легко доступен, прост в использовании и уже отлажен в глобальном масштабе. Если в будущем в нем будут обнаружены ошибки, они будут исправлены и доступны всем очень быстро, и вам не придется ничего делать.
2) Какие существуют процессы, гарантирующие, что вы никогда не пропустите вызов SafeDBString? Отсутствие его всего в одном месте может вызвать целый ряд проблем. Сколько вы собираетесь смотреть на эти вещи и учитывать, сколько зря потрачено на эти усилия, когда принятый правильный ответ так легко достичь.
3) Насколько вы уверены, что прикрыли каждый вектор атаки, о котором знает Microsoft (автор БД и библиотеки доступа), в вашей реализации SafeDBString ...
4) Насколько легко читать структуру sql? В примере используется + конкатенация, параметры очень похожи на string.Format, что более читабельно.
Кроме того, есть два способа выяснить, что на самом деле было запущено: развернуть собственную функцию LogCommand, простую функцию, не имеющую проблем с безопасностью , или даже посмотреть трассировку sql, чтобы выяснить, что, по мнению базы данных, действительно происходит.
Наша функция LogCommand проста:
Правильно это или нет, но он дает нам необходимую информацию без проблем с безопасностью.
источник
С помощью параметризованных запросов вы получаете больше, чем просто защиту от внедрения sql. Вы также получаете лучший потенциал кэширования плана выполнения. Если вы используете профилировщик запросов sql server, вы все равно можете видеть «точный sql, который запущен в базе данных», так что вы на самом деле ничего не теряете с точки зрения отладки ваших операторов sql.
источник
Я использовал оба подхода, чтобы избежать атак SQL-инъекций, и определенно предпочитаю параметризованные запросы. Когда я использовал составные запросы, я использовал библиотечную функцию, чтобы избежать переменных (например, mysql_real_escape_string), и не был бы уверен, что я охватил все в собственной реализации (как кажется, вы тоже).
источник
Вы не можете легко выполнить какую-либо проверку типа вводимых пользователем данных без использования параметров.
Если вы используете классы SQLCommand и SQLParameter для выполнения вызовов DB, вы все равно можете видеть выполняемый SQL-запрос. Посмотрите на свойство CommandText SQLCommand.
Я всегда мало подозреваю в применении индивидуального подхода к предотвращению внедрения SQL, когда параметризованные запросы настолько просты в использовании. Во-вторых, просто потому, что «так всегда делалось» не означает, что это правильный способ.
источник
Это безопасно только в том случае, если вам гарантировано, что вы собираетесь передать строку.
Что, если в какой-то момент вы не передаете строку? Что, если вы передадите только число?
В конечном итоге станет:
источник
Я бы использовал хранимые процедуры или функции для всего, поэтому вопрос не возникнет.
Там, где мне нужно вставить SQL в код, я использую параметры, и это единственное, что имеет смысл. Напомните несогласным, что есть хакеры, которые умнее их и имеют больше стимулов для взлома кода, который пытается их перехитрить. Используя параметры, это просто невозможно, да и не сложно.
источник
Совершенно согласен по вопросам безопасности.
Еще одна причина использовать параметры - эффективность.
Базы данных всегда будут компилировать ваш запрос и кэшировать его, а затем повторно использовать кешированный запрос (что, очевидно, быстрее для последующих запросов). Если вы используете параметры, то даже если вы используете другие параметры, база данных будет повторно использовать ваш кешированный запрос, поскольку он соответствует на основе строки SQL перед привязкой параметров.
Однако, если вы не привязываете параметры, строка SQL изменяется при каждом запросе (с разными параметрами), и она никогда не будет соответствовать тому, что находится в вашем кеше.
источник
По уже указанным причинам параметры - очень хорошая идея. Но мы ненавидим их использовать, потому что создание параметра и присвоение его имени переменной для последующего использования в запросе - это крушение тройного косвенного обращения.
Следующий класс является оболочкой для построителя строк, который вы обычно будете использовать для создания запросов SQL. Он позволяет вам писать параметризованные запросы без необходимости создавать параметр , поэтому вы можете сосредоточиться на SQL. Ваш код будет выглядеть так ...
Я надеюсь, вы согласитесь, что читаемость кода значительно улучшена, и на выходе получается правильно параметризованный запрос.
Класс выглядит так ...
источник
За очень короткое время, когда мне пришлось исследовать проблемы с SQL-инъекциями, я понял, что создание значения «безопасное» также означает, что вы закрываете дверь в ситуации, когда вам действительно могут понадобиться апострофы в своих данных - как насчет чьего-либо имени , например, О'Рейли.
Остались параметры и хранимые процедуры.
И да, вы всегда должны пытаться реализовать код наилучшим из известных вам способов, а не только так, как это всегда делалось.
источник
''
как литерал'
, поэтому ваша строка будет рассматриваться внутри как последовательность символовO'Reilly
. Это то, что БД будет хранить, извлекать, сравнивать с и т. Д. Если вы хотите показать пользователю его данные после того, как вы экранировали его, сохраните копию неэкранированной строки в приложении.Вот пара статей, которые могут помочь вам убедить ваших коллег.
http://www.sommarskog.se/dynamic_sql.html
http://unixwiz.net/techtips/sql-injection.html
Лично я предпочитаю никогда не позволять никакому динамическому коду касаться моей базы данных, требуя, чтобы все контакты осуществлялись через sps (а не тот, который использует динамический SQl). Это означает, что ничего, кроме того, что я дал пользователям разрешение, не может быть сделано, и что внутренние пользователи (за исключением очень немногих с производственным доступом для административных целей) не могут напрямую обращаться к моим таблицам и создавать хаос, красть данные или совершать мошенничество. Если вы запускаете финансовое приложение, это самый безопасный способ.
источник
Он может быть взломан, однако способ зависит от точных версий / патчей и т. Д.
Один из них уже упоминался - это ошибка переполнения / усечения, которую можно использовать.
Другое средство в будущем будет находить ошибки , похожие на другие базы данных - например , стек MySQL / PHP пострадали ускользающую проблему , потому что некоторые последовательности UTF8 может быть использованы для манипулирования функции замены - функция замены будет обмануто в введении символов инъекций.
В конце концов, заменяющий механизм безопасности полагается на ожидаемую, но не предполагаемую функциональность. Поскольку функциональность не была намеченной целью кода, высока вероятность того, что обнаруженная причуда нарушит вашу ожидаемую функциональность.
Если у вас много устаревшего кода, метод replace можно использовать в качестве временного решения, чтобы избежать длительного переписывания и тестирования. Если вы пишете новый код, нет оправдания.
источник
По возможности всегда используйте параметризованные запросы. Иногда даже простой ввод без использования каких-либо странных символов уже может создать SQL-инъекцию, если он не идентифицирован как ввод для поля в базе данных.
Так что просто позвольте базе данных выполнять свою работу по идентификации самого ввода, не говоря уже о том, что это также избавляет от проблем, когда вам действительно нужно вставлять странные символы, которые в противном случае были бы экранированы или изменены. В конце концов, он даже может сэкономить драгоценное время выполнения, так как вам не придется вычислять ввод.
источник
Я не видел, чтобы другие ответчики обращались к этой стороне вопроса «почему делать это самому - плохо», но рассмотрим атаку усечения SQL .
Также существует
QUOTENAME
функция T-SQL, которая может быть полезна, если вы не можете убедить их использовать параметры. Он улавливает множество (все?) Ускользнувших проблем qoute.источник
2 года спустя я вернулся ... Любой, кто считает параметры болезненными, может попробовать мое расширение VS, QueryFirst . Вы редактируете свой запрос в реальном файле .sql (Validation, Intellisense). Чтобы добавить параметр, вы просто вводите его прямо в свой SQL, начиная с '@'. Когда вы сохраняете файл, QueryFirst сгенерирует классы-оболочки, которые позволят вам выполнить запрос и получить доступ к результатам. Он найдет тип DB вашего параметра и сопоставит его с типом .net, который вы найдете в качестве входных данных для сгенерированных методов Execute (). Не может быть проще. Сделать это правильно радикально быстрее и проще, чем сделать это любым другим способом, а создание уязвимости SQL-инъекции становится невозможным или, по крайней мере, извращенно трудным. Есть и другие преимущества убийцы, такие как возможность удалять столбцы в вашей БД и сразу видеть ошибки компиляции в вашем приложении.
юридический отказ от ответственности: я написал QueryFirst
источник
Вот несколько причин использовать параметризованные запросы:
источник
Было несколько уязвимостей (я не могу вспомнить, какая это была база данных), связанных с переполнением буфера оператора SQL.
Я хочу сказать, что SQL-Injection - это больше, чем просто «уход от цитаты», и вы не знаете, что будет дальше.
источник
Еще одно важное соображение - отслеживание экранированных и неэкранированных данных. Существует множество приложений, веб-сайтов и прочих, которые, похоже, не отслеживают должным образом, когда данные являются необработанными - Unicode, & -кодированными, отформатированными HTML и т. Д. Очевидно, что станет трудно отслеживать, какие строки
''
закодированы, а какие нет.Это также проблема, когда вы в конечном итоге меняете тип некоторой переменной - возможно, раньше это было целое число, а теперь это строка. Теперь у тебя проблема.
источник