Как защитить эту функцию от 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

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

лучшим вариантом было бы запустить эту команду через безопасное соединение и дать ему не прав. Все усечь должен выполнить разрешение Alter таблицы. Если вы используете SQL 2005 вверх, вы также можете попробовать использовать хранимую процедуру с EXECUTE AS inside.


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;

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

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


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


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

предотвращает ли этот код SQL-инъекцию?

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

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


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

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

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

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


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

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

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


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

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

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

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

  • Не делайте ничего необратимого. Е. Г. не автоматически удалять таблицы. Вы можете флаг им выпало и e-mail ваш дБА. Она их уронит!--26-->после резервное копирование.

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


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


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

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