0

I have a process that supplies untrusted data to the database, so I've been trying to parameterize my queries.

When I use INSERT I can use something like the following, by way of example:

INSERT INTO [dbo].[Names]
           ([First_Name]
           ,[Last_Name])
VALUES ("Geoff", "Bridges")

I am, however, attempting to replicate that type of procedure using UPDATE.

UPDATE [dbo].[Names] SET 
    Last_Name,
    Last_Name
WHERE ID = 5
VALUES("Geoff", "Bridge");

I'm not quite sure how to approach this problem. Thanks for any assistance.

anakaine
  • 1,188
  • 2
  • 14
  • 30
  • 2
    So having read the documentation what parts of it didn't you understand? And secondly, from where are you calling this T-SQL? And if you are just substituting the `{}` with your own values, thats not parameterisation - thats string concatenation - you use actual parameters (`@ParamName`) for parameterisation – Dale K May 04 '21 at 02:14
  • 3
    your `UPDATE` query syntax is wrong. Please refer to [documentation](https://learn.microsoft.com/en-us/sql/t-sql/queries/update-transact-sql?view=sql-server-ver15) – Squirrel May 04 '21 at 02:15
  • 2
    The docs show plenty of examples of using constant values e.g. `UPDATE DimEmployee SET FirstName = 'Gail' WHERE EmployeeKey = 500; `. Or use an update with a join in the same way as your previous question about using values with delete. – Dale K May 04 '21 at 02:43
  • 1
    There is an [Examples](https://learn.microsoft.com/en-us/sql/t-sql/queries/update-transact-sql?view=sql-server-ver15#UpdateExamples) section in the documentation – Squirrel May 04 '21 at 02:49
  • 1
    But if you show how you plan to call this from code you may well find the correct solution is different again. – Dale K May 04 '21 at 02:51
  • My issue, with the example you have shown there Dale is that it's open to SQL injection. Which is why I'm seeking advice on how to parameterize. Eg if my "FirstName" input was set to ```Gail '; DROP TABLE Details;--``` then I'd have an issue. – anakaine May 04 '21 at 02:52
  • 1
    Your method is open to SQL injection also, as I have asked multiple times, explain from where you are calling this and we advise on the correct solution. – Dale K May 04 '21 at 02:57
  • The initial statement I had with the curly braces was exactly it. The curly braces were variables that would be substituted. My client is executing SQL directly - it's just being fed the variables. As it is an event driven process, it will only be updating, in this example, a single user at a time. I can substitute the names I've placed in there for now with my variables, but I'm struggling with how to parameterize it. If I was in SSMS for example, I could run the updates no worries, but that's not got untrusted input happening from me. – anakaine May 04 '21 at 02:57
  • 1
    That doesn't make anything any safer. Here is an example of [C# parameterisation](https://codereview.stackexchange.com/questions/164092/update-sql-database-with-using-statement-and-sqlparameters) which hopefully clarifies how you should be doing in, in whatever language you are using. – Dale K May 04 '21 at 02:59
  • What doesn't make it any safer? That example is using c#'s variable substitution to replace the values inside a query string, rather than parameterizing the SQL query itself. I'm using Node-Red in this case - and I'm executing a SQL query directly, almost the same as if it was being executed in SSMS. A comparison would be that I'd have to use server side javascript to attempt the same, and I suspect that has more issues than not. – anakaine May 04 '21 at 03:08

1 Answers1

1

SQL Parameterization can be achieved through different techniques depending on the programmatic context, from a pure SQL point of view, we simply need to declare the values as variables, then we can use those variables within your normal SQL commands.

DECLARE @firstName VARCHAR(50) = 'Geoff';
DECLARE @lastName VARCHAR(50) = 'Bridges';
DECLARE @recordId INT = 5;

INSERT INTO [dbo].[Names]
           ([First_Name]
           ,[Last_Name])
VALUES (@firstName, @lastName)

Updating the same record, when the ID is known to be 5:

UPDATE [dbo].[Names] SET 
    Last_Name = @lastName,
    First_Name = @firstName
WHERE ID = @recordId 

In most cases the declaration section is managed from your application logic, and not usually expressed explicitly in SQL.


UPDATE

After reading through the extended comments, its clear we need more information here. If you are constructing dynamic SQL scripts from your application logic, using declared parameters not going to provide much safety on its own, you will still need to sanitize the input if you wish to guard against SQL Injection attacks.

Most higher languages like C# will have a built in mechanism to sanitize and validate SQL parameter input, yes, ultimately this results at some level with the parameters being serialised into strings and injected into a SQL script, these mechanisms will prevent your script from executing if the type and or length validation fails.

The following is an example of an injection attack that will break even if you use parameterised syntax where you are only substituting the values:

User sets their first name as: hello'; DROP TABLE Names;SELECT '

Now, inject that into your script:

DECLARE @firstName VARCHAR(50) = 'hello'; DROP TABLE Names; SELECT '';
...

Whilst that may not result in the table being dropped, it is obvious that all sorts of commands could be executed in this way. I don't want to give you any specific malicious ideas, but it's common for attackers to grant permissions to external users or to otherwise exploit access to the underlying system, perhaps even to execute custom scripts directly from the command line.

  • this is one reason why you should NEVER use SA logins!

However, if the application were to sanitize the input data first, either through a framework, 3rd party library or manually, it should have escaped the name value and executed something like this instead:

DECLARE @firstName VARCHAR(50) = 'hello''; DROP TABLE Names; SELECT''';
...

Which would be a silly name for a user, but at least the table wasn't dropped.


Alternatively, if your request is to specifically use the VALUES Table Constructor syntax for the update, then that is possible too, though not advised...

UPDATE [dbo].[Names] SET 
    Last_Name = lName,
    First_Name = fName
FROM [dbo].[Names]
INNER JOIN 
(VALUES (@firstName, @lastName, @recordId)) as V(fName, lName, recId) ON V.recId = Names.ID
Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
  • Now that is a great reply. Thank you. What do you mean by SA logins? I'll have to do a bit of hunting on the sanitising - but the options are not too good on the platform I'm on, a combination of old production database versions and trying to sanitise SQL via javascript. – anakaine May 04 '21 at 05:22
  • As a general rule, you should NOT compose SQL from the browser, regardless of what you do to sanitize input, if your solution allows the browser to submit a SQL expression directly to the database, then your whole database is exposed. SA = 'Server Administrator' login, which has the highest privelidges. If you do not have a backend API, then please make sure that the user account that your runtime uses from the browser is locked down so that it cannot execute DDL statements – Chris Schaller May 04 '21 at 23:45
  • Chris, composing SQL from the browser is not the inference here. Think of Node-Red like having something available in Node, .Net Core, etc. Request comes in, http server middleware handles it, passes of request to SQL server. In this case Node-Red has the query in it that I need to run, and I'm attempting to load parameters from the http post request into it. For that part of the conversation I abstracted away that detail because I could be using a number of frameworks and have largely the same issue with creating the query. Thanks for clarifying what you meant by SA. – anakaine May 05 '21 at 04:57
  • This is an open question to ponder, please do not respond here... But if what you say is true @anakaine then I wonder why are aren't you using some sort of ORM framework, why are you manually composing these SQL statements, why would you try to support posting untrusted data directly into the database anyway... I wish you well, but this does not sound like good enterprise application design. – Chris Schaller May 05 '21 at 05:05
  • Every case has it's reasons. This is no exception. ORM is certainly a consideration, but it's not an option in this case, and thanks to me usually working at higher levels of abstraction I thus struggle with some of the lower bits being discussed here. I appreciate the note on it not being enterprise best practice, and I agree. Sometimes we have to work with what we have until we can affect change, however. – anakaine May 05 '21 at 05:33