Sample Header Ad - 728x90

Allow create table (dynamic name) and insert but nothing else

0 votes
1 answer
1494 views
I'm trying to find a way to allow an application to create tables and insert data into them on a SQL Server 2019 while protecting from injection attacks in case the app credentials would leak. My experience is limited when it comes to writing code that can run in parallel and writing dynamic sql that is protected from sql injection attacks. The table name is based on input from the application, i.e. if the input is 'nds' the table name should be lake.nds_raw_log. It is my understanding that there is no way to do this via directly granting permissions to the role for this application since creating tables is not separated from deleting or altering them. What I've come up with is executing a stored procedure as dbo. Sure it's not long but I have two issues with it: - it feels contrived which by my experience says that there is an easier way. - I believe that I need to run it as serializable to avoid orphan tables if I retrieve the wrong table when I query for my newly created table. This shouldn't actually be that big of an issue since it won't happen that often after the first start in production so maybe I shouldn't care about it.
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
So I guess this boils down to 3 questions: - Is this stored procedure actually safe from SQL injection attacks? - Is there an easier way to do it? - Is it correct that setting the transaction level as serializable will protect the code from selecting the wrong table when selecting from sys.tables?
Asked by Martin Riddar (196 rep)
Feb 14, 2022, 01:43 PM
Last activity: Feb 15, 2022, 05:33 AM