From 312ee4aa89bf1f9ab0956b47e5adfdd190e650a3 Mon Sep 17 00:00:00 2001 From: LukeMathWalker Date: Sun, 22 Aug 2021 16:54:41 +0100 Subject: [PATCH] Password hashing using SHA3-256. --- Cargo.lock | 27 ++++++++- Cargo.toml | 2 +- .../20210822143736_rename_password_column.sql | 1 + src/routes/newsletters.rs | 7 ++- tests/api/helpers.rs | 60 +++++++++++-------- tests/api/main.rs | 1 + tests/api/test_user.rs | 1 + 7 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 migrations/20210822143736_rename_password_column.sql create mode 100644 tests/api/test_user.rs diff --git a/Cargo.lock b/Cargo.lock index 2d2fa89..e9bb42d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,9 +329,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ + "block-padding", "generic-array", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "brotli-sys" version = "0.3.2" @@ -1087,6 +1094,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + [[package]] name = "language-tags" version = "0.3.2" @@ -1812,6 +1825,18 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer", + "digest", + "keccak", + "opaque-debug", +] + [[package]] name = "sharded-slab" version = "0.1.1" @@ -2690,7 +2715,7 @@ dependencies = [ "serde", "serde-aux", "serde_json", - "sha2", + "sha3", "sqlx", "thiserror", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 59fa5b9..94ef92c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,10 +32,10 @@ serde-aux = "1.0.1" unicode-segmentation = "1.7.1" validator = "0.12.0" rand = { version = "0.8", features=["std_rng"] } -sha2 = { version = "0.9" } tracing-actix-web = "0.4.0-beta.8" anyhow = "1.0.40" base64 = "0.13.0" +sha3 = "0.9" [dev-dependencies] once_cell = "1.7.2" diff --git a/migrations/20210822143736_rename_password_column.sql b/migrations/20210822143736_rename_password_column.sql new file mode 100644 index 0000000..e2dbd2c --- /dev/null +++ b/migrations/20210822143736_rename_password_column.sql @@ -0,0 +1 @@ +ALTER TABLE users RENAME password TO password_hash; diff --git a/src/routes/newsletters.rs b/src/routes/newsletters.rs index 24717de..18791b0 100644 --- a/src/routes/newsletters.rs +++ b/src/routes/newsletters.rs @@ -4,6 +4,7 @@ use crate::routes::error_chain_fmt; use actix_web::http::{HeaderMap, HeaderValue, StatusCode}; use actix_web::{web, HttpResponse, ResponseError}; use anyhow::Context; +use sha3::Digest; use sqlx::PgPool; #[derive(serde::Deserialize)] @@ -87,14 +88,16 @@ async fn validate_credentials( credentials: Credentials, pool: &PgPool, ) -> Result { + let password_hash = sha3::Sha3_256::digest(credentials.password.as_bytes()); + let password_hash = format!("{:x}", password_hash); let user_id: Option<_> = sqlx::query!( r#" SELECT user_id FROM users - WHERE username = $1 AND password = $2 + WHERE username = $1 AND password_hash = $2 "#, credentials.username, - credentials.password + password_hash ) .fetch_optional(pool) .await diff --git a/tests/api/helpers.rs b/tests/api/helpers.rs index 768489f..f3ce9e7 100644 --- a/tests/api/helpers.rs +++ b/tests/api/helpers.rs @@ -1,4 +1,5 @@ use once_cell::sync::Lazy; +use sha3::Digest; use sqlx::{Connection, Executor, PgConnection, PgPool}; use uuid::Uuid; use wiremock::MockServer; @@ -24,6 +25,7 @@ pub struct TestApp { pub port: u16, pub db_pool: PgPool, pub email_server: MockServer, + pub test_user: TestUser, } /// Confirmation links embedded in the request to the email API. @@ -44,10 +46,9 @@ impl TestApp { } pub async fn post_newsletters(&self, body: serde_json::Value) -> reqwest::Response { - let (username, password) = self.test_user().await; reqwest::Client::new() .post(&format!("{}/newsletters", &self.address)) - .basic_auth(username, Some(password)) + .basic_auth(&self.test_user.username, Some(&self.test_user.password)) .json(&body) .send() .await @@ -77,14 +78,6 @@ impl TestApp { let plain_text = get_link(&body["TextBody"].as_str().unwrap()); ConfirmationLinks { html, plain_text } } - - pub async fn test_user(&self) -> (String, String) { - let row = sqlx::query!("SELECT username, password FROM users LIMIT 1",) - .fetch_one(&self.db_pool) - .await - .expect("Failed to create test users."); - (row.username, row.password) - } } pub async fn spawn_app() -> TestApp { @@ -122,26 +115,14 @@ pub async fn spawn_app() -> TestApp { .await .expect("Failed to connect to the database"), email_server, + test_user: TestUser::generate(), }; - add_test_user(&test_app.db_pool).await; + test_app.test_user.store(&test_app.db_pool).await; test_app } -async fn add_test_user(pool: &PgPool) { - sqlx::query!( - "INSERT INTO users (user_id, username, password) - VALUES ($1, $2, $3)", - Uuid::new_v4(), - Uuid::new_v4().to_string(), - Uuid::new_v4().to_string(), - ) - .execute(pool) - .await - .expect("Failed to create test users."); -} - async fn configure_database(config: &DatabaseSettings) -> PgPool { // Create database let mut connection = PgConnection::connect_with(&config.without_db()) @@ -163,3 +144,34 @@ async fn configure_database(config: &DatabaseSettings) -> PgPool { connection_pool } + +pub struct TestUser { + user_id: Uuid, + username: String, + password: String, +} + +impl TestUser { + pub fn generate() -> Self { + Self { + user_id: Uuid::new_v4(), + username: Uuid::new_v4().to_string(), + password: Uuid::new_v4().to_string(), + } + } + + async fn store(&self, pool: &PgPool) { + let password_hash = sha3::Sha3_256::digest(self.password.as_bytes()); + let password_hash = format!("{:x}", password_hash); + sqlx::query!( + "INSERT INTO users (user_id, username, password_hash) + VALUES ($1, $2, $3)", + self.user_id, + self.username, + password_hash, + ) + .execute(pool) + .await + .expect("Failed to store test user."); + } +} diff --git a/tests/api/main.rs b/tests/api/main.rs index 43409e1..8a7a20c 100644 --- a/tests/api/main.rs +++ b/tests/api/main.rs @@ -3,3 +3,4 @@ mod helpers; mod newsletter; mod subscriptions; mod subscriptions_confirm; +mod test_user; diff --git a/tests/api/test_user.rs b/tests/api/test_user.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/api/test_user.rs @@ -0,0 +1 @@ +