#database #sql #sqlite #mysql #postgres

concatsql

A secure library for SQLite, MySQL and PostgreSQL

4 releases

new 0.2.1 Nov 25, 2020
0.2.0 Nov 22, 2020
0.1.1 Nov 16, 2020
0.1.0 Nov 16, 2020

#235 in Database interfaces

MIT license

94KB
2K SLoC

ConcatSQL

Actions Status Crates.io Documentation License

ConcatSQL(concatsql) is a secure SQL database library.
You can use string concatenation to prevent SQL injection.

Documentation

Supported databases:

You can configure the database backend in Cargo.toml:

[dependencies]
concatsql = { version = "<version>", features = ["<postgres|mysql|sqlite>"] }

Examples

Normal value

let id     = String::from("42");    // User supplied input
let passwd = String::from("pass");  // User supplied input

let sql = prep!("SELECT name FROM users WHERE id=") + &id + prep!(" AND passwd=") + &passwd;
assert_eq!(sql.simulate(), "SELECT name FROM users WHERE id='42' AND passwd='pass'");

for row in conn.rows(&sql).unwrap() {
    assert_eq!(row.get(0).unwrap(),      "Alice");
    assert_eq!(row.get("name").unwrap(), "Alice");
}

Illegal value

let id     = String::from("42");             // User supplied input
let passwd = String::from("'' or 1=1; --");  // User supplied input

let sql = prep!("SELECT name FROM users WHERE id=") + &id + prep!(" AND passwd=") + &passwd;
assert_eq!(sql.simulate(), "SELECT name FROM users WHERE id='42' AND passwd=''''' or 1=1; --'");

for row in conn.rows(&sql).unwrap() {
    unreachable!();
}

If you did not use the prep macro

Cannot compile ... secure!

let id     = String::from("42");
let passwd = String::from("' or 1=1; --");
let sql = "SELECT name FROM users WHERE id=".to_string() + &id + " AND passwd='" + &passwd + "';";
conn.execute(&sql).unwrap();  // error

When using prep!(<String>)

Cannot compile ... secure!

let age = String::from("50 or 1=1; --");
let sql = prep!("SELECT name FROM users WHERE age < ") + prep!(&age);  // error

Why can this library prevent SQL injection?

This is because it is achieved using Operator Overloading rather than simple string concatenation.
The prep macro returns the library's own type(WrapString).
For example, if you combine this WrapString type with a String type, the escaped String type will be combined and a new WrapString will be returned.

let foobar: WrapString = prep!("foo") + String::from("bar");

Is it impossible to implement in other languages?

It seems that it can be implemented in other languages as long as it supports operator overloading.
However, if the developer writes the following, the input from the attacker will not be escaped correctly and the attack will be successful.

prep("SELECT * FROM users WHERE id=" + id + " AND PASSWORD=" + password)

That is, it can be implemented in any language that can distinguish between hard-coding(&'static str) and user input(String) at compile time.

License

MIT

Dependencies

~0.4–1.8MB
~37K SLoC