diff options
| author | Joris Guyonvarch | 2026-04-17 22:53:02 +0200 |
|---|---|---|
| committer | Joris Guyonvarch | 2026-04-17 22:53:02 +0200 |
| commit | 648d073e1b8f4838f147c0520024bd453921a25c (patch) | |
| tree | 3a477d88c3f80a68d6477d9fe5644cc475c0cd81 | |
| parent | 2a6bcee45086bca9128489de19908984ea1be0da (diff) | |
Remove signing login token
It’s enough to use a safe crypto lib. But augment the token size to
upper bound.
| -rw-r--r-- | Cargo.lock | 10 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | config.json | 3 | ||||
| -rw-r--r-- | src/crypto/mod.rs | 1 | ||||
| -rw-r--r-- | src/crypto/signed.rs | 71 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/model/config.rs | 2 | ||||
| -rw-r--r-- | src/routes.rs | 5 | ||||
| -rw-r--r-- | src/utils/cookie.rs | 18 |
9 files changed, 13 insertions, 99 deletions
@@ -189,7 +189,6 @@ dependencies = [ "chrono", "env_logger", "hex", - "hmac", "http-body-util", "hyper", "hyper-util", @@ -517,15 +516,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -10,7 +10,6 @@ bcrypt = "0.17" chrono = "0.4" env_logger = "0.11" hex = "0.4" -hmac = "0.12" http-body-util = "0.1" hyper = { version = "1.7", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } diff --git a/config.json b/config.json index 20bad2e..86a19b5 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,3 @@ { - "secure_cookies": false, - "auth_secret": "1QAZa8RSgogsakXSa0hgEeyA7xqnvo91" + "secure_cookies": false } diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs deleted file mode 100644 index 41e9259..0000000 --- a/src/crypto/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod signed; diff --git a/src/crypto/signed.rs b/src/crypto/signed.rs deleted file mode 100644 index 436f3d1..0000000 --- a/src/crypto/signed.rs +++ /dev/null @@ -1,71 +0,0 @@ -use hex; -use hmac::{Hmac, Mac}; -use sha2::Sha256; -use std::str; -use std::time::{SystemTime, UNIX_EPOCH}; - -const SEP: &str = "-"; - -pub fn sign(key: &str, raw: &str) -> Result<String, String> { - let nonce = get_nonce()?; - let joined = format!("{nonce}{SEP}{raw}"); - let signature = get_signature(key, &joined)?; - Ok(format!("{signature}{SEP}{joined}")) -} - -pub fn verify(key: &str, signed: &str) -> Result<String, String> { - let mut iter = signed.split(SEP); - match (iter.next(), iter.next()) { - (Some(signature), Some(nonce)) => { - let raw = iter.collect::<Vec<&str>>().join(SEP); - if signature == get_signature(key, &format!("{nonce}{SEP}{raw}"))? { - Ok(raw) - } else { - Err("Signature does not match".to_string()) - } - } - _ => Err("Malformed signed".to_string()), - } -} - -fn get_signature(key: &str, message: &str) -> Result<String, String> { - let mut mac = Hmac::<Sha256>::new_from_slice(key.as_bytes()) - .map_err(|e| format!("Error initializing MAC: {e}"))?; - mac.update(message.as_bytes()); - let result = mac.finalize(); - Ok(hex::encode(result.into_bytes())) -} - -fn get_nonce() -> Result<String, String> { - Ok(SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|e| format!("Failure getting unix expoch: {e}"))? - .as_millis() - .to_string()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sign_and_validate() { - let key = "xagrlBUobnTj32Rm8tvmsZ6mh8qLfip5".to_string(); - assert_eq!(verify(&key, &sign(&key, "").unwrap()), Ok("".to_string())); - assert_eq!( - verify(&key, &sign(&key, "hello").unwrap()), - Ok("hello".to_string()) - ); - assert_eq!( - verify(&key, &sign(&key, "with-sep").unwrap()), - Ok("with-sep".to_string()) - ); - } - - #[test] - fn fail_when_key_mismatch() { - let key1 = "xagrlBUobnTj32Rm8tvmsZ6mh8qLfip5".to_string(); - let key2 = "8KJBK6axEr9wQ390GgdWA8Pjn8FwILDa".to_string(); - assert!(verify(&key1, &sign(&key2, "hello").unwrap()).is_err()); - } -} diff --git a/src/main.rs b/src/main.rs index 70bac81..1cc2384 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ use tokio::net::TcpListener; mod assets; mod controller; -mod crypto; mod db; mod jobs; mod model; diff --git a/src/model/config.rs b/src/model/config.rs index f40b0fb..ba923d6 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -4,7 +4,6 @@ use std::str::FromStr; #[derive(Clone)] pub struct Config { - pub auth_secret: String, pub db_path: String, pub secure_cookies: bool, pub socket_address: SocketAddr, @@ -12,7 +11,6 @@ pub struct Config { pub fn from_env() -> Result<Config, String> { Ok(Config { - auth_secret: read_string("AUTH_SECRET")?, db_path: read_string("DB_PATH")?, secure_cookies: read_bool("SECURE_COOKIES")?, socket_address: read_socket_address("SOCKET_ADDRESS")?, diff --git a/src/routes.rs b/src/routes.rs index 7107a60..8abe1b4 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -49,7 +49,7 @@ pub async fn routes( "icon.png" => file("assets/icon.png", "image/png").await, _ => controller::utils::not_found(), }, - _ => match connected_user(&config, &db_conn, &request).await { + _ => match connected_user(&db_conn, &request).await { Some(user) => { let wallet = Wallet { db_conn, @@ -67,12 +67,11 @@ pub async fn routes( } async fn connected_user( - config: &Config, db_conn: &Connection, request: &Request<Incoming>, ) -> Option<User> { let cookie = request.headers().get("COOKIE")?.to_str().ok()?; - let login_token = cookie::extract_token(config, cookie).ok()?; + let login_token = cookie::extract_token(cookie).ok()?; db::users::get_by_login_token(db_conn, login_token.to_string()).await } diff --git a/src/utils/cookie.rs b/src/utils/cookie.rs index e21e7d4..1ca3b73 100644 --- a/src/utils/cookie.rs +++ b/src/utils/cookie.rs @@ -1,25 +1,27 @@ use hex; use rand_core::{OsRng, TryRngCore}; -use crate::crypto::signed; use crate::model::config::Config; -const TOKEN_BYTES: usize = 20; +// We consider that it’s unfeasible to guess a token from 128 bit long (=16 bytes) to 256 bit (=32 bytes) with safe margin. +const TOKEN_BYTES: usize = 32; pub fn login(config: &Config, token: &str) -> Result<String, String> { - let signed_token = signed::sign(&config.auth_secret, token)?; - Ok(cookie(config, &signed_token, 365 * 24 * 60 * 60)) + Ok(cookie(config, &token, 365 * 24 * 60 * 60)) } pub fn logout(config: &Config) -> String { cookie(config, "", 0) } -pub fn extract_token(config: &Config, cookie: &str) -> Result<String, String> { +pub fn extract_token(cookie: &str) -> Result<String, String> { let mut xs = cookie.split('='); - xs.next(); - let signed_cookie = xs.next().ok_or("Error extracting cookie")?; - signed::verify(&config.auth_secret, signed_cookie) + if xs.next() != Some("TOKEN") { + Err("Error extracting cookie".to_string()) + } else { + let token = xs.next().ok_or("Error extracting cookie")?; + Ok(token.to_string()) + } } pub fn generate_token() -> Result<String, String> { |
