diff --git a/Cargo.lock b/Cargo.lock index 7170fde..f02711f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -810,6 +810,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "secstr" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04f657244f605c4cf38f6de5993e8bd050c8a303f86aeabff142d5c7c113e12" +dependencies = [ + "libc", +] + [[package]] name = "serde" version = "1.0.208" @@ -1099,6 +1108,7 @@ dependencies = [ "http 1.1.0", "maud", "rand", + "secstr", "sha2", "thiserror", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 38c1c51..417a7d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ futures-util = "0.3.30" hmac = "0.12.1" http = "1.1.0" rand = "0.8.5" +secstr = "0.5.1" sha2 = "0.10.8" thiserror = "1.0.63" tower-cookies = "0.10.0" diff --git a/src/surf.rs b/src/surf.rs index 99a681f..aca908c 100644 --- a/src/surf.rs +++ b/src/surf.rs @@ -1,5 +1,6 @@ use futures_util::future::BoxFuture; use http::{Request, Response}; +use secstr::SecStr; use std::{ sync::Arc, task::{Context, Poll}, @@ -15,7 +16,7 @@ use crate::{guard::GuardService, Error, Token}; #[derive(Clone)] pub(crate) struct Config { - pub(crate) secret: String, + pub(crate) secret: SecStr, pub(crate) cookie_name: String, pub(crate) expires: Expiration, pub(crate) header_name: String, @@ -50,7 +51,7 @@ impl Surf { pub fn new(secret: impl Into) -> Self { Self { config: Config { - secret: secret.into(), + secret: SecStr::from(secret.into()), cookie_name: "csrf_token".into(), expires: Expiration::Session, header_name: "X-CSRF-Token".into(), diff --git a/src/token.rs b/src/token.rs index 600ea2f..37371e5 100644 --- a/src/token.rs +++ b/src/token.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use base64::prelude::*; use hmac::{Hmac, Mac}; use rand::prelude::*; +use secstr::SecStr; use sha2::Sha256; use tower_cookies::{Cookie, Cookies}; @@ -80,7 +81,10 @@ impl Token { type HmacSha256 = Hmac; -pub(crate) fn create_token(secret: &str, identifier: impl Into) -> Result { +pub(crate) fn create_token( + secret: &SecStr, + identifier: impl Into, +) -> Result { let random = BASE64_STANDARD.encode(get_random_value()); let message = format!("{}!{}", identifier.into(), random); let result = sign_and_encode(secret, &message)?; @@ -89,7 +93,7 @@ pub(crate) fn create_token(secret: &str, identifier: impl Into) -> Resul Ok(token) } -pub(crate) fn validate_token(secret: &str, cookie: &str, token: &str) -> Result { +pub(crate) fn validate_token(secret: &SecStr, cookie: &str, token: &str) -> Result { let mut parts = token.splitn(2, '.'); let received_hmac = parts.next().unwrap_or(""); @@ -112,8 +116,8 @@ fn get_random_value() -> [u8; 64] { [42u8; 64] } -fn sign_and_encode(secret: &str, message: &str) -> Result { - let mut mac = HmacSha256::new_from_slice(secret.as_bytes())?; +fn sign_and_encode(secret: &SecStr, message: &str) -> Result { + let mut mac = HmacSha256::new_from_slice(secret.unsecure())?; mac.update(message.as_bytes()); let result = BASE64_STANDARD.encode(mac.finalize().into_bytes()); @@ -128,7 +132,8 @@ mod tests { #[test] fn create_token() -> Result<()> { - let token = super::create_token("super-secret", "identifier")?; + let secret = SecStr::from("super-secret"); + let token = super::create_token(&secret, "identifier")?; let parts = token.splitn(2, '.').collect::>(); assert_eq!(parts.len(), 2); @@ -136,7 +141,7 @@ mod tests { let message = format!("{}!{}", "identifier", BASE64_STANDARD.encode([42u8; 64])); assert_eq!(parts[1], message); - let signature = sign_and_encode("super-secret", &message)?; + let signature = sign_and_encode(&secret, &message)?; assert_eq!(parts[0], signature); Ok(())