jjo*_*ras 15 sql sql-server stored-procedures sql-server-2008
我正在尝试创建一个查询sys.tables表的简单存储过程.
CREATE PROCEDURE dbo.test
@dbname NVARCHAR(255),
@col NVARCHAR(255)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
USE @dbname
SELECT TOP 100 *
FROM sys.tables
WHERE name = @col
GO
Run Code Online (Sandbox Code Playgroud)
这似乎不起作用,因为我应该在USE @dbname之后放置GO,但这会终止此过程的创建吗?如何将此数据库选择放入此过程中,以便用户可以将数据库名称作为此proc的参数?
Mar*_*ith 26
如果使用EXEC @Var
(不带括号 - 即不使用 EXEC (@Var)
),SQL Server将查找与传入的名称匹配的存储过程@Var
.您可以使用三部分命名.
如果sys.sp_executesql
使用三部分名称调用,则将上下文设置为调用它的数据库.
因此,您可以使用零 SQL注入风险执行此操作,如下所示.
CREATE PROCEDURE dbo.test @dbname SYSNAME,
@col SYSNAME
AS
SET NOCOUNT, XACT_ABORT ON;
DECLARE @db_sp_executesql NVARCHAR(300) = QUOTENAME(@dbname) + '.sys.sp_executesql'
EXEC @db_sp_executesql N'
SELECT TOP 100 *
FROM sys.columns
WHERE name = @col',
N'@col sysname',
@col = @col
Run Code Online (Sandbox Code Playgroud)
即使上述情况不可能,我仍然认为完全可以在这里以安全的方式使用动态SQL.
CREATE PROCEDURE dbo.test
@dbname SYSNAME, /*Use Correct Datatypes for identifiers*/
@col SYSNAME
AS
SET NOCOUNT ON
SET XACT_ABORT ON
IF DB_ID(@dbname) IS NULL /*Validate the database name exists*/
BEGIN
RAISERROR('Invalid Database Name passed',16,1)
RETURN
END
DECLARE @dynsql nvarchar(max)
/*Use QUOTENAME to correctly escape any special characters*/
SET @dynsql = N'USE '+ QUOTENAME(@dbname) + N'
SELECT TOP 100 *
FROM sys.tables
WHERE name = @col'
/*Use sp_executesql to leave the WHERE clause parameterised*/
EXEC sp_executesql @dynsql, N'@col sysname', @col = @col
Run Code Online (Sandbox Code Playgroud)
3Da*_*ave 16
至少有两种方法可以做到这一点:
使用case/switch语句(或者,在我的示例中,一个朴素的if..else
块)将参数与数据库列表进行比较,并基于此执行using语句.这具有限制proc可以访问已知集的数据库的优点,而不是允许访问用户帐户有权访问的任何内容和所有内容.
declare @dbname nvarchar(255);
set @dbname = 'db1';
if @dbname = 'db1'
use db1;
else if @dbname = 'db2'
use db2;
Run Code Online (Sandbox Code Playgroud)动态SQL.我讨厌动态SQL.这是一个巨大的安全漏洞,几乎没有必要.(从正确的角度来看:在17年的专业发展中,我从未必须部署使用动态SQL的生产系统).如果您决定使用此路由,请将动态调用/创建的代码限制为using语句,并调用另一个存储过程执行实际工作.using
由于范围规则,您不能仅自动动态执行语句.
declare @sql nvarchar(255);
set @sql = 'using '+@dbname+'; exec mydatabase..do_work_proc;';
Run Code Online (Sandbox Code Playgroud)当然,在你的例子中,你可以做到
set @sql='select * from '+@dbname+'.sys.tables';
Run Code Online (Sandbox Code Playgroud)
在.<schema_name>.
解析运算符可以查询在不同数据库中的对象,而无需使用use
声明.
在一些非常非常罕见的情况下,可能需要允许sproc使用任意数据库.在我看来,唯一可接受的用途是代码生成器,或某种数据库分析工具,它不能提前知道所需的信息.
更新原来你不能use
在存储过程中,将动态SQL作为唯一明显的方法.不过,我考虑使用
select top 100 * from db_name.dbo.table_name
Run Code Online (Sandbox Code Playgroud)
而不是一个use
.