Hi @iwcoetzer,

Araq is correct, your properQuote doesn't do anything. To get a big portion of 
SQL injection safety, always use parameterised queries. Never combine strings 
with SQL. As soon as you start combining text for queries, you're in trouble.

For example, this is a bad idea:
    
    
    proc `%` (frmt: SQL, values: openarray[string]): SQL =
        let v = values.mapIt(properQuote(it))
        type StrSeq = seq[string]
        result = SQL(string(frmt) % StrSeq(v))
    
    
    Run

Ideally the query should be able to be run without being changed at all, and 
only parameters changed. As well as better sanitation, this also means your 
queries don't need to be prepared each time they're executed and a whole host 
of other performance bonuses. For example, each preparation requires tokenising 
and sending the query to the server, processing the queries (without running 
them), then waiting for a response back! Additionally, parameterised queries 
will tell you if there is some type issue relating to parameters and the table 
way earlier than if you're combining text.

The obdc library has support for handling parameterised queries natively and 
therefore avoid injection. What I would do personally is update the query with 
paramters directly:
    
    
    #[ prepare a SQL script ]#
    query.statement = "SELECT TOP 100 * FROM $1 (NOLOCK) WHERE EffectivePeriod 
>= ?effectivePeriod" % ["dbo.DimContract"]
    query.params["effectivePeriod"] = "201901" # Or maybe this wants to be an 
actual date rather than a string?
    
    
    Run

Note that there is still a string concatination for the table name. In this 
case you have two options:

  1. Make damn sure your table names can't be entered/edited or corrupted by 
users. I would be very cautious when taking this approach.
  2. Use dynamic SQL To select from a table, and **pass the table name as a 
parameter** as above to the dynamic SQL. This is, as I understand it, the 
'preferred' approach, though it does seem a tad unweildy, it is very powerful. 
A quick tutorial on how to do this is here: 
[https://www.sqlservertutorial.net/sql-server-stored-procedures/sql-server-dynamic-sql](https://www.sqlservertutorial.net/sql-server-stored-procedures/sql-server-dynamic-sql)/
 So, in their example the line 
    
        SET @table = N'production.products';
    
    Run

would be more like 
    
        SET @table = ?myTableName;
    
    Run




You still need to be careful to only use parameterised data in dynamic SQL 
though, so don't pass in strings to be combined into dynamic SQL unless there 
is no way there's user input.

To make things a bit easier, I've recently added fire-and-forget queries to 
odbc so you can use type checked parameters for instant queries if you don't 
want to create a query object but want type safe parameter checks.
    
    
    let results = connection.dbq("SELECT ?value1, ?value2", [("value1", 17), 
("value2", 24)]
    
    
    Run

@Araq: I wrote the odbc library for a few reasons:

  1. The standard db_* libraries just handle all data as strings, which is fine 
for quick queries but not viable (IMHO) for lots of data work. I put in a fair 
bit of effort to make sure you could directly use native Nim types both for 
parameters and all the results from the server and they are type checked 
against the ODBC types.
  2. Odbc allows named parameters such as ?myParam rather than just ?.
  3. Finally - and most damningly as I've detailed above, db_odbc doesn't use 
odbc parameterisation and instead [just uses string 
concatination]([https://github.com/nim-lang/Nim/blob/b6924383df63c91f0ad6baf63d0b1aa84f9329b7/lib/impure/db_odbc.nim#L194)](https://github.com/nim-lang/Nim/blob/b6924383df63c91f0ad6baf63d0b1aa84f9329b7/lib/impure/db_odbc.nim#L194\)).



Yes some effort is made to escape strings, but this is to me wholey worse than 
the guaranteed parameter isolation that native odbc parameters give you. There 
is absolutely no way to escape parameterised queries when used properly.

Reply via email to