
Compile Time Prevention of SQL-Injections in Rust - jesperht
https://polyfloyd.net/post/compile-time-prevention-of-sql-injections/
======
Tuna-Fish
This doesn't actually work. It is possible to produce objects with 'static
lifetime references at runtime.

What &'static means is that whatever the reference is pointing at will never
be modified or go out of scope. One way to provide this is to put it in the
read-only part of the executable, which is what literals do. Another is to use
into_boxed_str() [1] and Box::leak() [2] to leak the string and thus make sure
it will never be modified or freed. Neither function is unsafe, while
Box::leak() is still only in nightly.

[1]: [https://doc.rust-
lang.org/std/string/struct.String.html#meth...](https://doc.rust-
lang.org/std/string/struct.String.html#method.into_boxed_str) [2]:
[https://doc.rust-
lang.org/std/boxed/struct.Box.html#method.l...](https://doc.rust-
lang.org/std/boxed/struct.Box.html#method.leak)

~~~
Paul-ish
This does seem to be the case. Here is an example I was able to create:

    
    
      use std::io::stdin;
    
      fn main() {
          let mut s = String::new();
          stdin().read_line(&mut s).expect("Did not enter a correct string");
          let sql_example = format!("SELECT * FROM users WHERE username={}", s);
          let x = Box::new(sql_example);
          let static_ref: &'static str = Box::leak(x);
          println!("{}", static_ref)
      }
    

Note the type on our variable "static_ref". It is a static str, meaning it
could be an argument to the code in the blog post. No "unsafe" blocks either.

When I run it

    
    
      Input: ' OR '1'='1
      Output: SELECT * FROM users WHERE username=' OR '1'='1
    

The technique might be still useful if it was only allowed in debug builds to
reduce boilerplate during debugging in a system that tried to use types to
solve the issue.

------
Eridrus
This is a pretty natural thing to want, but is not actually usable in real
world settings.

If your SQL has to be computed at compile time, how do you implement any sort
of search where you will have a variable number of ANDS & ORs?

~~~
gnud
You could try to create some sort of SQL-builder API where each fragment must
be static, and each fragment contains its own placeholders.

Then you can use conditionals and loops to decide which fragments are
included.

~~~
pc86
Or you can just use runtime SQL injection prevention methods?

~~~
coldtea
Or just have an employee carefully monitor user input manually, and have them
propagate it to the backend with a delay.

That is, sure, it's not like Rust takes the option to check for injection at
runtime.

~~~
swirepe
Our team found it's easier to just not have any users.

~~~
OskarS
I find that not having any users helps a lot with load balancing also.

------
chopin
I think the main problem (don't know whether this is possible in Rust which I
am not familiar with) is that untrusted input is passed around as strings at
all (Java, which I am familiar with, does this). I'd prefer:

\- Getting untrusted input as a separate type

\- Having only a controlled way to put instances of this type into an SQL
query

~~~
bluejekyll
You pretty much just nailed exactly what this post is demonstrating about
Rust. The lifetime associated with the different types indicates the
provenance of the variables.

Basically, the 'static lifetime guarantees that the query string was built at
compile time. And then it allows the parameters to be from user input. To
Rust, these are effectively different types due to the restrictions on the
function definition, which restricts the first parameter to 'static, and the
list of SQL parameters can come from anywhere (static or dynamic runtime).

~~~
icebraining
Yeah, but using "built at compile time" as a synonym for "safe" is pretty
crude. A runtime string composed of the concatenation of two compiled strings
is still safe, yet not allowed here.

~~~
kbsletten
That seems easy enough to fix, no? Change the API to allow a runtime generated
list of compile-time strings, _et viola_. Though, I doubt that's as safe as
you might imagine. Imagine a return oriented programming-like technique using
static SQL strings. Difficult, for sure, but not impossible.

------
kibwen
It's a neat little hack, though it seems a bit restrictive. I was expecting a
blog post about how, by using ownership, it should be possible to create an
API that both requires untrusted data to be escaped and prevents double-
escaping (though you could probably achieve pretty much the same in any
statically-typed language).

------
alexandernst
Is it me or the person that wrote that post has no idea what SQL inject
actually is?

~~~
empath75
I don’t understand at all what the difference is between the two functions.

~~~
borntyping
The second function only accepts strings defined at compile time, meaning it
can't be called with strings created at runtime (i.e. any string containing
user input).

~~~
empath75
So what to you do if you need to make a sql call based on user input?

~~~
gnud
Use parameters, of course. Using SQL parameters for untrusted input is the
only sane way to avoid SQL injections.

------
olavk
Is this serious? It seems to prevent SQL injection by only allowing statically
defined strings to be interpolated. So basically not allowing any kind of
dynamic or user input.

~~~
kuschku
That’s the point. You should never use string interpolation with strings
defined at runtime for SQL.

Always build your queries out of strings defined at compile time and use them
as prepared statements with parameters.

~~~
pjmlp
That is the theory, sadly it doesn't work everywhere on the SQL statement.

So we always end up with a mix of prepared statements and string manipulation.

