Member 8057273 Ответов: 4

Хранимая процедура выдает ошибку


У меня есть хранимая процедура MSSQL, как показано ниже:
ALTER procedure [dbo].[GetDataFromTable]
(
@rowval varchar(50),
@tablename varchar(50),
@oby varchar(50)
)
as
begin
EXEC('Select top (' + @rowval + ') * from '+@tablename+ 'ORDER BY sno DESC')
end

При выполнении он выдает следующую ошибку: Msg 156, Уровень 15, состояние 1, строка 1 неправильный синтаксис рядом с ключевым словом "BY".
Примечание: @rowval представляет количество строк, которые должны быть извлечены, @tablename представляет имя таблицы, @oby представляет столбец, на основе которого должен быть выполнен порядок. Примечание: Я использую ASP.Net с C# на интерфейсе для запуска этой процедуры и с использованием MSSQL 2008 R2 Express Edition на бэкэнде

Что я уже пробовал:

ALTER procedure [dbo].[GetDataFromTable]
(
@rowval varchar(50),
@tablename varchar(50),
@oby varchar(50)
)
as
begin
EXEC('Select top (' + @rowval + ') * from '+@tablename+ 'ORDER BY '+@oby+' DESC')
end

4 Ответов

Рейтинг:
2

OriginalGriff

Пространства, мой друг, пространства.
Изменить это:

from '+@tablename+ 'ORDER BY '

До настоящего времени:
from '+@tablename+ ' ORDER BY '
Чтобы отделить имя таблицы от порядка по тексту...

Но я надеюсь, что вы дезинфицируете свои входные данные, а не просто пропускаете пользовательский ввод - или вы скоро потеряете свою БД - этот код широко открыт для атаки SQL-инъекций...


Member 8057273

Расскажите, пожалуйста, как санировать процедуру безопасности.

Vincent Maverick Durano

google "sql injection" или "как это предотвратить"

Рейтинг:
2

Patrice T

Как уже было сказано, вы пропустили пробел перед заказом.

EXEC('Select top (' + @rowval + ') * from '+@tablename+ ' ORDER BY sno DESC')

То, как вы используете параметры, открывает дверь для SQL-инъекции.
https://en.wikipedia.org/wiki/SQL_injection[^]
http://www.w3schools.com/sql/sql_injection.asp[^]

[обновление]
Цитата:
Привет, я знаю, что такое SQL-инъекция и как она работает.
Мы не можем догадаться, что вы знаете, что это за опасная практика, и ваше использование, если оно не делает ее менее опасной.

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


Member 8057273

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

Рейтинг:
2

Richard Deeming

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

Чтобы предотвратить SQL-инъекция[^] уязвимость, вам необходимо проверить как имя таблицы, так и имя столбца" order by". Это относительно легко, когда у вас есть одно имя столбца, без модификатора" ASC "или" DESC". Если вы хотите упорядочить по нескольким столбцам или контролировать последовательность сортировки, это становится намного сложнее.

Вам также нужно будет изменить @rowval параметр к целому числу и использовать процедуры sp_executesql[^] чтобы передать это в ваш динамический запрос в качестве параметра. (Этот TOP оператор может принимать переменную, поэтому для этого вам не нужен динамический SQL.)

Предполагая, @oby содержит только одно имя столбца, без модификатора, тогда что-то вроде этого должно работать:

ALTER PROCEDURE [dbo].[GetDataFromTable]
(
    @rowval int,
    @tablename varchar(50),
    @oby varchar(50)
)
As
BEGIN
DECLARE @ObjectID int, @RealTableName sysname, @RealSchemaName sysname, @RealColumnName;
DECLARE @Query nvarchar(max), @Parameters nvarchar(max);
    
    SET NOCOUNT ON;
    SET @ObjectID = OBJECT_ID(@tablename);
    If @ObjectID Is Null RAISERROR('The table "%s" was not found.', 16, 1, @tablename);
    
    SELECT
        @RealTableName = QUOTENAME(T.name),
        @RealSchemaName = QUOTENAME(S.name)
    FROM
        sys.tables As T
        INNER JOIN sys.schemas As S
        ON S.schema_id = T.schema_id
    WHERE
        T.object_id = @ObjectID
    And
        T.type = 'U'
    ;
    
    If @@ROWCOUNT = 0 RAISERROR('The table "%s" was not found.', 16, 1, @tablename);
    
    SELECT
        @RealColumnName = QUOTENAME(name)
    FROM
        sys.columns
    WHERE
        object_id = @ObjectID
    And
        name = @oby
    ;
    
    If @@ROWCOUNT = 0 RAISERROR('The column "%s" was not found in the table "%s".', 16, 1, @oby, @tablename);
    
    SET @Query = N'SELECT TOP (@rowval) * FROM ' + @RealSchemaName + N'.' + @RealTableName + N' ORDER BY ' + @RealColumnName + N' DESC';
    SET @Parameters = N'@rowval int';
    
    EXEC sp_executesql @Query, @Parameters, @rowvalue = @rowval;
END

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


Рейтинг:
1

rhgarner

отсутствует ведущее пространство перед "заказом"

проходя в имятаблицы = кранты, то SQL-код будет стать ...от фубарадера мимо...

использование exec на SQL-операторах, построенных на основе переданных параметров, может быть очень опасным. Пожалуйста, убедитесь, что все передаваемые данные санированы, чтобы предотвратить инъекцию.

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

напр..

declare @s varchar(100)
set @s = 'Select top (' + @rowval + ') * from ' + @tablename + ' ORDER BY ' + @oby + ' DESC'

print @s
exec(@s)


- Ричаред


Member 8057273

Расскажите, пожалуйста, как санировать процедуру безопасности.

rhgarner

существует множество методов. поиск на этом сайте "sql-инъекции" дает довольно много результатов.

один из моих любимых мультфильмов на эту тему-от xkcd. https://xkcd.com/327/

Member 8057273

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