Как защитить эту функцию от SQL-инъекций? - PullRequest
10 голосов
/ 07 декабря 2009
public static bool TruncateTable(string dbAlias, string tableName)
{
    string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName);
    return ExecuteNonQuery(dbAlias, sqlStatement) > 0;
}

Ответы [ 11 ]

22 голосов
/ 07 декабря 2009

Наиболее распространенная рекомендация для борьбы с внедрением SQL - использовать параметр запроса SQL (несколько человек в этой теме предложили это).

Это неправильный ответ в данном случае. Нельзя использовать параметр запроса SQL для имени таблицы в инструкции DDL.

Параметры запроса SQL могут использоваться только вместо литерального значения в выражении SQL. Это стандартно для каждой реализации SQL.

Моя рекомендация по защите от внедрения SQL при наличии имени таблицы заключается в проверке входной строки по списку известных имен таблиц.

Вы можете получить список допустимых имен таблиц из INFORMATION_SCHEMA:

SELECT table_name 
FROM INFORMATION_SCHEMA.Tables 
WHERE table_type = 'BASE TABLE'
  AND table_name = @tableName

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

Вы также можете проверить имя таблицы по списку определенных таблиц, которые вы определили как приемлемые для усечения вашего приложения, как @John Buchanan предлагает .

Даже после проверки того, что tableName существует в качестве имени таблицы в вашей РСУБД, я бы также предложил разделить имя таблицы на тот случай, если вы используете имена таблиц с пробелами или специальными символами. В Microsoft SQL Server разделителями идентификаторов по умолчанию являются квадратные скобки:

string sqlStatement = string.Format("TRUNCATE TABLE [{0}]", tableName);

Теперь вы подвержены риску внедрения SQL, только если tableName соответствует реальной таблице, и вы фактически используете квадратные скобки в именах ваших таблиц!

6 голосов
/ 07 декабря 2009

Насколько я знаю, вы не можете использовать параметризованные запросы для выполнения операторов DDL / указания имен таблиц, по крайней мере, в Oracle или Sql Server. Что бы я сделал, если бы у меня была сумасшедшая функция TruncateTable, которая должна была быть защищена от SQL-инъекций, это сделать хранимую процедуру, которая проверяет, является ли входная таблица безопасной для усечения.


-- Sql Server specific!
CREATE TABLE TruncableTables (TableName varchar(50))
Insert into TruncableTables values ('MyTable')

go

CREATE PROCEDURE MyTrunc @tableName varchar(50)
AS
BEGIN

declare @IsValidTable int
declare @SqlString nvarchar(50)
select @IsValidTable = Count(*) from TruncableTables where TableName = @tableName

if @IsValidTable > 0
begin
 select @SqlString = 'truncate table ' + @tableName
 EXECUTE sp_executesql @SqlString
end
END
3 голосов
/ 11 марта 2011
CREATE OR REPLACE PROCEDURE truncate(ptbl_name IN VARCHAR2) IS
  stmt VARCHAR2(100);
BEGIN
  stmt := 'TRUNCATE TABLE '||DBMS_ASSERT.SIMPLE_SQL_NAME(ptbl_name);
  dbms_output.put_line('<'||stmt||'>');
  EXECUTE IMMEDIATE stmt;
END;
3 голосов
/ 07 декабря 2009

Если вы разрешаете вводить пользовательский ввод в эту функцию через переменную tablename, я не думаю, что SQL-инъекция - ваша единственная проблема.

Лучшим вариантом было бы запустить эту команду через собственное безопасное соединение и вообще не предоставлять ей права SELECT. Все, что требуется для запуска TRUNCATE - это разрешение ALTER TABLE. Если вы используете SQL 2005 и выше, вы также можете попробовать использовать хранимую процедуру с EXECUTE AS внутри.

2 голосов
/ 07 декабря 2009

Используйте хранимую процедуру. Любая приличная библиотека БД (MS Enterprise Library - это то, что я использую) будет правильно обрабатывать экранирующие строковые параметры.

Кроме того, повторно: параметризованные запросы: я предпочитаю НЕ переустанавливать мое приложение, чтобы исправить проблему с БД. Хранение запросов в виде буквенных строк в вашем источнике увеличивает сложность обслуживания.

1 голос
/ 07 декабря 2009

Существуют и другие посты, которые помогут с внедрением SQL, поэтому я добавлю их, но еще одна вещь, которую стоит рассмотреть, - как вы будете обрабатывать разрешения для этого. Если вы предоставляете пользователям роли db + owner или db_ddladmin, чтобы они могли усекать таблицы, то просто избегать стандартных атак с использованием SQL-инъекций недостаточно. Хакер может отправить другие имена таблиц, которые могут быть допустимыми, но которые вы не хотите обрезать.

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

Обычно TRUNCATE TABLE не используется при обычном повседневном использовании приложений. Он используется для сценариев ETL или во время обслуживания базы данных. Единственная ситуация, в которой я мог бы предположить, что она будет использоваться во фронтальном приложении, была бы, если бы вы позволили пользователям загружать таблицу, специфичную для этого пользователя, для целей загрузки, но даже тогда я, вероятно, использовал бы другое решение.

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

1 голос
/ 07 декабря 2009

Использовать параметризованные запросы.

1 голос
/ 07 декабря 2009

Посмотрите по этой ссылке

Этот код предотвращает внедрение SQL?

Удалите ненужные строки tableName.

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

0 голосов
/ 07 декабря 2009

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

Почему вы позволите этому случиться? Если вы разрешаете какую-то внешнюю сущность (конечный пользователь, другая система, что?) назвать таблицу, которую нужно удалить, почему бы вам просто не дать им права администратора.

Если вы создаете и удаляете таблицы, чтобы обеспечить некоторую функциональность для конечного пользователя, не позволяйте им предоставлять имена для объектов базы данных напрямую. Помимо SQL-инъекций у вас будут проблемы с конфликтами имен и т. Д. Вместо этого генерируйте реальные имена таблиц самостоятельно (например, DYNTABLE_00001, DYNTABLE_00002, ...) и сохраняйте таблицу, которая связывает их с именами, предоставленными пользователем.


Некоторые примечания по генерации динамического SQL для операций DDL:

  • В большинстве СУБД вам придется использовать динамический SQL и вставлять имена таблиц в виде текста. Будьте особенно осторожны.

  • Использовать идентификаторы в кавычках ([] в MS SQL Server, "" во всех СУБД, совместимых с ANSI). Это упростит предотвращение ошибок, вызванных неверными именами.

  • Сделайте это в хранимых процедурах и проверьте, все ли указанные объекты действительны.

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

  • Избегайте, если можете. Если вы не можете, сделайте все возможное, чтобы минимизировать права на других (не динамические) таблицы, которые будут иметь обычные пользователи.

0 голосов
/ 07 декабря 2009

Если вы не можете использовать параметризованные запросы (и должны это делать) ... простая замена всех экземпляров 'with' 'должна сработать.

string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName.Replace("'", "''")); 
...