Как защитить эту функцию от SQL-инъекций?

public static bool TruncateTable(string dbAlias, string tableName)
{
    string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName);
    return ExecuteNonQuery(dbAlias, sqlStatement) > 0;
}

11 ответов

Решение

Наиболее распространенная рекомендация для борьбы с 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 соответствует реальной таблице, и вы фактически используете квадратные скобки в именах ваших таблиц!

Насколько я знаю, вы не можете использовать параметризованные запросы для выполнения операторов 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
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;

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

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

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

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

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

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

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

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

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

Посмотрите на эту ссылку

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

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

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

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

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

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


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

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

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

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

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

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

Вы можете использовать SQLParameter для передачи значения tableName. Насколько я знаю и тестировал, SQLParameter берет на себя все проверки параметров и, таким образом, отключает возможность внедрения.

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

string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName.Replace("'", "''")); 
Другие вопросы по тегам