diff --git a/Cargo.toml b/Cargo.toml index 0e4b171..cb28f49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +actix-web="4.3.1" +actix-files="0.6.2" +actix-web-validator = "5.0.1" +serde = { veresion = "1.0.160", features = ["derive"] } +serde_json = "1.0.96" +handlebars = { version="4.3.6", features = ["dir_source"] } +rusqlite = "0.29.0" diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..4932c9a --- /dev/null +++ b/Readme.md @@ -0,0 +1,23 @@ +# Sql-Injection-Test + +> Version: 1.0.23110.1 \ +> Created by: Jali +> Last Modified by: Jali + +## About + +This project implements a very simple web-service, that is vulnerable to an SQL +injection attack. In this case a simple web-page is protected by a user name and +password, and the password is checked by requesting the user name and password +from the database, and check if they exist. If they do, users are granted access +to the web-page, if not they are thrown back to the login page. + +The SQL queries, however, are vulnerable to SQL injection. So a user can gain +access by simply putting a statement such as + +```wurst' OR '1'='1``` + +into the password field. The where clause '1'='1' will always be true, and +therefore the statement always returns a list of all possible users. + +The example creates an in memory database with users Alice and Bob. diff --git a/src/main.rs b/src/main.rs index e7a11a9..35ad422 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,122 @@ -fn main() { - println!("Hello, world!"); +use actix_files::Files; +use actix_web::{web, App, HttpResponse, HttpServer}; +use handlebars::Handlebars; +use serde::Deserialize; +use serde_json::json; + +#[derive(Deserialize)] +struct Credentials { + username: String, + password: String, +} + +fn init_sql() -> rusqlite::Connection { + let connection = rusqlite::Connection::open_in_memory().unwrap(); + connection + .execute( + "CREATE TABLE IF NOT EXISTS users (username TEXT, password TEXT, UNIQUE(username));", + (), + ) + .unwrap(); + connection + .execute("INSERT OR IGNORE INTO users VALUES ('Alice', 'test');", ()) + .unwrap(); + connection + .execute( + "INSERT OR IGNORE INTO users VALUES ('Bob', 'lovesApples');", + (), + ) + .unwrap(); + connection +} + +async fn index(hb: web::Data>) -> HttpResponse { + let data = json!({ + "project_name": "Book Store Login", + }); + + let body = hb.render("index", &data).unwrap(); + HttpResponse::Ok().body(body) +} + +async fn validate_login( + hb: web::Data>, + cred: web::Form, +) -> HttpResponse { + let connection = init_sql(); + let query = format!( + "SELECT * FROM users WHERE username = '{}' AND password = '{}';", + cred.username, cred.password + ); + println!("{}", query); + let mut stmt = connection.prepare(query.as_str()).unwrap(); + let users = stmt + .query_map([], |row| { + Ok(Credentials { + username: row.get(0)?, + password: row.get(1)?, + }) + }) + .unwrap(); + + if users.count() > 0 { + println!("Match!"); + return storepage(hb).await; + } + + println!("No Match!"); + index(hb).await +} + +async fn storepage(hb: web::Data>) -> HttpResponse { + let data = json!({ + "project_name": "Book Store", + "books":[ + { + "name":"Harry Potter", + "author":"J K Rowlings", + "image_path":"/static/images/download.jpeg" + }, + { + "name":"Lord of the ring", + "author":"Tolken", + "image_path": "/static/images/lord_of.jpeg" + }, + { + "name":"Americanah", + "author":"Chimamada Adichie", + "image_path":"/static/images/americanah.jpeg" + }, + { + "name":"Elon Musk", + "author":"#####", + "image_path":"/static/images/elon.jpeg" + }, + ] + }); + + let body = hb.render("store", &data).unwrap(); + HttpResponse::Ok().body(body) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let mut handlebars = Handlebars::new(); + handlebars + .register_templates_directory(".html", "./static/") + .unwrap(); + let handlebars_ref = web::Data::new(handlebars); + + println!("listening on port 8080"); + HttpServer::new(move || { + App::new() + .app_data(handlebars_ref.clone()) + .service(Files::new("/static", "static").show_files_listing()) + //.service(web::resource("/login").route(web::post().to(validate_login))) + .route("/", web::post().to(validate_login)) + .route("/", web::get().to(index)) + }) + .bind("0.0.0.0:8080")? + .run() + .await } diff --git a/static/css/index.css b/static/css/index.css new file mode 100644 index 0000000..dcf0bdd --- /dev/null +++ b/static/css/index.css @@ -0,0 +1,16 @@ +.cats { + display: flex; +} + +.books { + border: 1px solid grey; + min-width: 200px; + min-height: 350px; + margin: 5px; + padding: 5px; + text-align: center; +} + +.book > img { + width: 190px; +} diff --git a/static/images/americanah.jpeg b/static/images/americanah.jpeg new file mode 100644 index 0000000..a5db43a Binary files /dev/null and b/static/images/americanah.jpeg differ diff --git a/static/images/download.jpeg b/static/images/download.jpeg new file mode 100644 index 0000000..94543df Binary files /dev/null and b/static/images/download.jpeg differ diff --git a/static/images/elon.jpeg b/static/images/elon.jpeg new file mode 100644 index 0000000..1a7f277 Binary files /dev/null and b/static/images/elon.jpeg differ diff --git a/static/images/lord_of.jpeg b/static/images/lord_of.jpeg new file mode 100644 index 0000000..ce04ddb Binary files /dev/null and b/static/images/lord_of.jpeg differ diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..7f28357 --- /dev/null +++ b/static/index.html @@ -0,0 +1,23 @@ + + + + + + {{project_name}} + + + + +

{{project_name}}

+
+ + +
+ + +
+ +
+ + + \ No newline at end of file diff --git a/static/store.html b/static/store.html new file mode 100644 index 0000000..b8cb266 --- /dev/null +++ b/static/store.html @@ -0,0 +1,23 @@ + + + + + + {{project_name}} + + + + +

{{project_name}}

+
+ {{#each books}} +
+

{{this.name}}

+ +

Author: {{this.author}}

+
+ {{/each}} +
+ + + \ No newline at end of file