From dc08e1486c919dc8f168543adeb86cfe1f3b645e Mon Sep 17 00:00:00 2001 From: mrw1593 Date: Sat, 13 May 2023 12:46:09 -0400 Subject: Make secrets more secret --- Cargo.lock | 8 +++ Cargo.toml | 2 + sqlx-data.json | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 +- src/services/crypto.rs | 36 +++++------ src/services/db.rs | 5 +- src/services/mod.rs | 1 + src/services/secrets.rs | 13 ++++ 8 files changed, 200 insertions(+), 22 deletions(-) create mode 100644 src/services/secrets.rs diff --git a/Cargo.lock b/Cargo.lock index 1fb82f4..582a115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,6 +511,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dotenvy" version = "0.15.6" @@ -1226,7 +1232,9 @@ name = "rust-pw-server" version = "0.1.0" dependencies = [ "actix-web", + "dotenv", "exun", + "hex", "raise", "rand", "rust-argon2", diff --git a/Cargo.toml b/Cargo.toml index 0c40ad7..4366420 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ thiserror = "1" rust-argon2 = "1" uuid = { version = "1", features = [ "v4", "fast-rng", "serde" ] } raise = "2" +dotenv = "0.15" rand = "0.8" sqlx = { version = "0.6", features = [ "runtime-actix-rustls", "mysql", "uuid", "offline" ] } +hex = "0.4" exun = "0.1" diff --git a/sqlx-data.json b/sqlx-data.json index 187cd79..eab27cc 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1,5 +1,82 @@ { "db": "MySQL", + "016610abc2d730ab748db57c97ea3d7c2cc38d1f3dbc7dfef86742454d4b15f1": { + "describe": { + "columns": [ + { + "name": "user_id", + "ordinal": 0, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4231 + }, + "max_size": 16, + "type": "String" + } + }, + { + "name": "username", + "ordinal": 1, + "type_info": { + "char_set": 224, + "flags": { + "bits": 4101 + }, + "max_size": 1020, + "type": "VarString" + } + }, + { + "name": "password_hash", + "ordinal": 2, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4241 + }, + "max_size": 255, + "type": "Blob" + } + }, + { + "name": "password_salt", + "ordinal": 3, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4241 + }, + "max_size": 255, + "type": "Blob" + } + }, + { + "name": "password_version", + "ordinal": 4, + "type_info": { + "char_set": 63, + "flags": { + "bits": 33 + }, + "max_size": 10, + "type": "Long" + } + } + ], + "nullable": [ + false, + false, + false, + false, + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "SELECT user_id, username, password_hash, password_salt, password_version\n\t\t FROM users WHERE user_id = ?" + }, "0e1cd93286d20bbaa3e25dbec43b5e28cfbe6506d2d47432259e228f7f92c867": { "describe": { "columns": [ @@ -80,6 +157,83 @@ }, "query": "UPDATE users SET\n\t\tpassword_hash = ?,\n\t\tpassword_salt = ?,\n\t\tpassword_version = ?\n\t\tWHERE user_id = ?" }, + "e87cb729ff241fce8e1ddcd43519105910b61184b1ed755a437bd511eac4f9cf": { + "describe": { + "columns": [ + { + "name": "user_id", + "ordinal": 0, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4231 + }, + "max_size": 16, + "type": "String" + } + }, + { + "name": "username", + "ordinal": 1, + "type_info": { + "char_set": 224, + "flags": { + "bits": 4101 + }, + "max_size": 1020, + "type": "VarString" + } + }, + { + "name": "password_hash", + "ordinal": 2, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4241 + }, + "max_size": 255, + "type": "Blob" + } + }, + { + "name": "password_salt", + "ordinal": 3, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4241 + }, + "max_size": 255, + "type": "Blob" + } + }, + { + "name": "password_version", + "ordinal": 4, + "type_info": { + "char_set": 63, + "flags": { + "bits": 33 + }, + "max_size": 10, + "type": "Long" + } + } + ], + "nullable": [ + false, + false, + false, + false, + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "SELECT user_id, username, password_hash, password_salt, password_version\n\t\t FROM users WHERE username = ?" + }, "ed4a40d7a9417985e6552d368146f5791717a41c16921f5074dfa14b637a9209": { "describe": { "columns": [], diff --git a/src/main.rs b/src/main.rs index dff3f3f..d454a8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,8 @@ use services::*; #[actix_web::main] async fn main() -> Result<(), RawUnexpected> { - let sql_pool = db::initialize("password_database", "dbuser", "Demo1234").await?; + let db_url = secrets::database_url()?; + let sql_pool = db::initialize(&db_url).await?; HttpServer::new(move || { App::new() diff --git a/src/services/crypto.rs b/src/services/crypto.rs index 580e83a..7ad2ce0 100644 --- a/src/services/crypto.rs +++ b/src/services/crypto.rs @@ -3,24 +3,23 @@ use std::hash::Hash; use argon2::{hash_raw, verify_raw}; use exun::RawUnexpected; -/// A custom pepper used to hide passwords -static PEPPER: [u8; 16] = [ - 0x98, 0x7f, 0x6f, 0xce, 0x20, 0x76, 0x2c, 0x8a, 0xae, 0xf6, 0xee, 0x45, 0xb3, 0x6b, 0x1f, 0x69, -]; +use crate::services::secrets::pepper; /// The configuration used for hashing and verifying passwords -static CONFIG: argon2::Config<'_> = argon2::Config { - hash_length: 32, - lanes: 4, - mem_cost: 5333, - time_cost: 4, - secret: &PEPPER, +fn config<'a>(pepper: &'a [u8]) -> argon2::Config<'a> { + argon2::Config { + hash_length: 32, + lanes: 4, + mem_cost: 5333, + time_cost: 4, + secret: pepper, - ad: &[], - thread_mode: argon2::ThreadMode::Sequential, - variant: argon2::Variant::Argon2i, - version: argon2::Version::Version13, -}; + ad: &[], + thread_mode: argon2::ThreadMode::Sequential, + variant: argon2::Variant::Argon2i, + version: argon2::Version::Version13, + } +} /// A password hash and salt for a user #[derive(Debug, Clone, PartialEq, Eq)] @@ -43,8 +42,8 @@ impl PasswordHash { let salt: [u8; 16] = rand::random(); let salt = Box::from(salt); - - let hash = hash_raw(password, &salt, &CONFIG)?.into_boxed_slice(); + let pepper = pepper()?; + let hash = hash_raw(password, &salt, &config(&pepper))?.into_boxed_slice(); Ok(Self { hash, @@ -78,11 +77,12 @@ impl PasswordHash { /// Check if the given password is the one that was hashed pub fn check_password(&self, password: &str) -> Result { + let pepper = pepper()?; Ok(verify_raw( password.as_bytes(), &self.salt, &self.hash, - &CONFIG, + &config(&pepper), )?) } } diff --git a/src/services/db.rs b/src/services/db.rs index 80335c4..b24c640 100644 --- a/src/services/db.rs +++ b/src/services/db.rs @@ -33,9 +33,8 @@ impl TryFrom for User { } /// Intialize the connection pool -pub async fn initialize(db: &str, user: &str, password: &str) -> Result { - let url = format!("mysql://{user}:{password}@localhost/{db}"); - MySqlPool::connect(&url).await.unexpect() +pub async fn initialize(db_url: &str) -> Result { + MySqlPool::connect(db_url).await.unexpect() } pub async fn user_id_exists<'c>( diff --git a/src/services/mod.rs b/src/services/mod.rs index 57146d8..09d2159 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1,3 +1,4 @@ pub mod crypto; pub mod db; pub mod id; +pub mod secrets; diff --git a/src/services/secrets.rs b/src/services/secrets.rs new file mode 100644 index 0000000..e4a1ca1 --- /dev/null +++ b/src/services/secrets.rs @@ -0,0 +1,13 @@ +use std::env; + +use exun::*; + +pub fn pepper() -> Result, RawUnexpected> { + let pepper = env::var("SECRET_SALT")?; + let pepper = hex::decode(pepper)?; + Ok(pepper.into_boxed_slice()) +} + +pub fn database_url() -> Result { + env::var("DATABASE_URL").unexpect() +} -- cgit v1.2.3