diff --git a/.gitignore b/.gitignore index ea8c4bf..345a792 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +test.sqlite diff --git a/Cargo.lock b/Cargo.lock index 12f4cf7..c7c0e25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,10 +284,34 @@ version = "0.1.0" dependencies = [ "actix-web", "anyhow", + "libinjection", "serde", + "sqlite", "tokio", ] +[[package]] +name = "bindgen" +version = "0.63.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -349,12 +373,32 @@ dependencies = [ "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -429,6 +473,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -520,6 +570,27 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +[[package]] +name = "git2" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "h2" version = "0.3.20" @@ -615,12 +686,84 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "libgit2-sys" +version = "0.14.2+1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libinjection" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a9a90ce65224c87515f603d1eed413909886dec98f2e9985044f0b88e26039" +dependencies = [ + "bindgen", + "git2", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "local-channel" version = "0.1.3" @@ -667,6 +810,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -688,6 +837,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -713,6 +872,24 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -742,6 +919,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.3.0" @@ -864,6 +1047,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -945,6 +1134,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -989,6 +1184,36 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "sqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ddda64c469a257a3b31298805427784d992c226c94b81003f96e8b122286ad" +dependencies = [ + "libc", + "sqlite3-sys", +] + +[[package]] +name = "sqlite3-src" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc95a51a1ee38839599371685b9d4a926abb51791f0bc3bf8c3bb7867e6e454" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "sqlite3-sys" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2752c669433e40ebb08fde824146f50d9628aa0b66a3b7fc6be34db82a8063b" +dependencies = [ + "libc", + "sqlite3-src", +] + [[package]] name = "syn" version = "1.0.109" @@ -1157,6 +1382,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -1169,6 +1400,17 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 84e421f..b16e90b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,5 @@ tokio = { version = "1.29", features = ["macros", "rt-multi-thread", "sync"] } anyhow = "1.0" actix-web = "4" serde = { version = "1.0.183", features = ["derive"] } +sqlite = "0.31.0" +libinjection = "0.3.2" diff --git a/README.md b/README.md index acf040f..23dde6b 100644 --- a/README.md +++ b/README.md @@ -21,18 +21,19 @@ Registers a new account. #### Responses -| Response | Description | -|----------|------------------------------------------| -| 200 | User created successfully | -| 400 | Formal error in the request | -| 409 | The requested username is already in use | -| 500 | Internal server error | +| Response | Description | +|----------|----------------------------------------------------------| +| 200 | User created successfully | +| 400 | Formal error in the request | +| 403 | Rejected for security reasons (sql injection protection) | +| 409 | The requested username is already in use | +| 500 | Internal server error | ### `/authenticate` Authenticates using an existing account. -#### Payload +#### Content / Payload | Field | Description | |------------|-----------------------------------| @@ -41,13 +42,14 @@ Authenticates using an existing account. #### Responses -| Response | Description | -|----------|----------------------------------------------------| -| 200 | Authenticated successfully | -| 400 | Formal error in the request | -| 401 | Authentication failure (wrong password) | -| 404 | There was no account found with the specified name | -| 500 | Internal server error | +| Response | Description | +|----------|----------------------------------------------------------| +| 200 | Authenticated successfully | +| 400 | Formal error in the request | +| 401 | Authentication failure (wrong password) | +| 403 | Rejected for security reasons (sql injection production) | +| 404 | There was no account found with the specified name | +| 500 | Internal server error | --- diff --git a/src/api.rs b/src/api.rs index 28b23d6..41d1fc4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -18,6 +18,7 @@ struct AuthenticateData { /// Possible Responses: /// * 200 -> Authentication was successfull /// * 401 -> Authentication failed (wrong password) +/// * 403 -> The request was rejected due to security reasons /// * 404 -> User not found /// * 500 -> Internal Server Error #[post("/authenticate")] @@ -44,6 +45,7 @@ async fn authenticate( ResponseAuthenticate::Success => HttpResponse::Ok().finish(), ResponseAuthenticate::UserNotFound => HttpResponse::NotFound().finish(), ResponseAuthenticate::WrongPassword => HttpResponse::Unauthorized().finish(), + ResponseAuthenticate::Rejected => HttpResponse::Forbidden().finish(), }, _ => HttpResponse::InternalServerError().finish(), }, @@ -59,6 +61,7 @@ struct RegisterData { /// Possible Responses: /// * 200 -> User created successfully +/// * 403 -> The request was rejected due to security reasons /// * 409 -> The requested username is already in use /// * 500 -> Internal Server Error #[post("/register")] @@ -81,6 +84,7 @@ async fn register(data: web::Data, body: web::Json) -> i Response::Register(r) => match r { ResponseRegister::Success => HttpResponse::Ok().finish(), ResponseRegister::NameAlreadyInUse => HttpResponse::Conflict().finish(), + ResponseRegister::Rejected => HttpResponse::Forbidden().finish(), }, _ => HttpResponse::InternalServerError().finish(), }, diff --git a/src/backend.rs b/src/backend.rs index 9eb4925..3d36167 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,11 +1,25 @@ use anyhow::Result; +use libinjection::sqli; use crate::call::{Response, ResponseAuthenticate, ResponseDelete, ResponseRegister}; -pub struct Backend {} +pub struct Backend { + db: sqlite::Connection, +} + +fn is_sql_injection(string: &String) -> bool { + match sqli(string) { + Some((is_injection, _)) => is_injection, + None => true, // this could be a false positive, but that would be better than an SQLi + } +} impl Backend { pub fn register(&mut self, username: &String, password: &String) -> Result { + if is_sql_injection(username) { + return Ok(Response::Register(ResponseRegister::Rejected)); + } + println!( " Registered Account: (Username: '{}' | Password: '{}')", username, password @@ -14,6 +28,10 @@ impl Backend { } pub fn authenticate(&mut self, username: &String, password: &String) -> Result { + if is_sql_injection(username) { + return Ok(Response::Authenticate(ResponseAuthenticate::Rejected)); + } + println!( " Authentication Request: (Username: '{}' | Password: '{}')", username, password @@ -23,12 +41,28 @@ impl Backend { } pub fn delete(&mut self, username: &String) -> Result { + if is_sql_injection(username) { + return Ok(Response::Delete(ResponseDelete::Rejected)); + } + println!(" Deletion Request: (Username: '{}')", username); println!(" -> Accepted"); Ok(Response::Delete(ResponseDelete::Success)) } - pub fn new() -> Result { - Ok(Self {}) + pub fn new(database: &std::path::Path) -> Result { + let conn = sqlite::open(database)?; + + conn.execute( + "CREATE TABLE IF NOT EXISTS BaseAuth ( + id INTEGER, + username TEXT, + salt TEXT, + password TEXT, + PRIMARY KEY(id) + );", + )?; + + Ok(Self { db: conn }) } } diff --git a/src/call.rs b/src/call.rs index 96e8bf8..c10691c 100644 --- a/src/call.rs +++ b/src/call.rs @@ -14,6 +14,7 @@ pub enum Call { pub enum ResponseRegister { Success, NameAlreadyInUse, + Rejected, } #[derive(Debug)] @@ -21,12 +22,14 @@ pub enum ResponseAuthenticate { Success, UserNotFound, WrongPassword, + Rejected, } #[derive(Debug)] pub enum ResponseDelete { Success, UserNotFound, + Rejected, } #[derive(Debug)] diff --git a/src/main.rs b/src/main.rs index afceb8f..ed125e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use anyhow::Result; async fn main() -> Result<()> { println!("Starting BaseAuth server v0.1"); - let backend = backend::Backend::new()?; + let backend = backend::Backend::new(&std::path::Path::new("test.sqlite"))?; let tx = call::start_worker(backend).await?; api::start_worker(8080, tx).await?;