我正在尝试找到一种方法,允许应用程序在 SQL Server 2019 上创建表并将数据插入其中,同时防止注入攻击,以防应用程序凭据泄漏。在编写可以并行运行的代码和编写不受 sql 注入攻击的动态 sql 时,我的经验是有限的。
表名基于来自应用程序的输入,即如果输入为“nds”,则表名应为lake.nds_raw_log。
据我了解,没有办法通过直接授予此应用程序的角色权限来执行此操作,因为创建表与删除或更改它们没有分开。
我想出的是作为 dbo 执行存储过程。当然它不长,但我有两个问题:
- 感觉很做作,根据我的经验,有一种更简单的方法。
- 我相信如果我在查询新创建的表时检索到错误的表,我需要将它作为可序列化运行以避免孤立表。这实际上不应该是一个大问题,因为它不会在第一次开始生产后经常发生,所以也许我不应该关心它。
create procedure [lake].[create_terminal_raw_log_table]
(
@terminal_name nvarchar(100)
)
with execute as 'dbo'
as
begin try
set transaction isolation level serializable
begin transaction
--create table
declare @dynamic_sql nvarchar(1000) =
'create table [lake].' + quotename(@terminal_name) + '
(
id bigint not null,
[timestamp] datetime2(3) not null,
cmd varbinary(max) not null
);'
exec sp_executesql @dynamic_sql
/*get name of new table, this is why I believe that I need serializable isolation
since other tables can be created in parallel*/
declare @table_name nvarchar(100) =
(
select top 1
[name] as table_name
from sys.tables
order by create_date desc
)
--rename table
declare
@old_name nvarchar(100) = '[lake].' + @table_name,
@new_name nvarchar(100) = @table_name + '_raw_log'
begin try
exec sp_rename
@objname = @old_name,
@newname = @new_name
end try
begin catch
set @dynamic_sql = 'drop table ' + @old_name
exec sp_executesql @dynamic_sql
;throw
end catch
--create primary key
set @dynamic_sql = 'alter table [lake].' + @new_name + ' add constraint pk__' + @new_name + ' primary key(id)'
exec sp_executesql @dynamic_sql
commit transaction
end try
begin catch
rollback --I thought a rollback would occur when I throw after dropping the table but that doesn't seem to be the case
;throw
end catch
所以我想这可以归结为3个问题:
- 这个存储过程实际上可以免受 SQL 注入攻击吗?
- 有更简单的方法吗?
- 将事务级别设置为可序列化将保护代码在从 sys.tables 中选择时不会选择错误的表是否正确?
除非我遗漏了业务需求或代码中的细节,否则我认为您正在使您的程序变得比必要的复杂得多。
不需要创建表,然后查找表的名称,然后重命名表,然后添加主键约束,所有这些都作为单独的步骤,包装在事务中以确保一致性。相反,您可以一步完成。
关于您的代码的其他一些代码审查类型注释:
nvarchar
变量来支持 unicode,但使用“常规”单引号进行分配。要支持 unicode 字符串,您需要使用N'
前缀来引用 unicode 字符串。CREATE TABLE
sys
在sp_executesql
.这是我的程序版本:
确保你测试!
您需要进行一些快速的健全性测试,以确保您的程序确实有效。我喜欢确保使用 unicode 字符(我总是使用表情符号)和任何其他特定问题(如 SQL 注入、对象名称中的空格、最小或最大长度等)进行测试。
例如:
返回这些结果: