Dont use sha hash for password reset token (fixes #3491) (#3795)

This commit is contained in:
Nutomic 2023-08-02 19:02:53 +02:00 committed by GitHub
parent 27be1efb74
commit 2d0f77af59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 13 additions and 40 deletions

2
Cargo.lock generated
View file

@ -2678,7 +2678,6 @@ dependencies = [
"serde_json",
"serde_with",
"serial_test",
"sha2",
"strum_macros",
"task-local-extensions",
"tokio",
@ -2711,7 +2710,6 @@ dependencies = [
"serde_json",
"serde_with",
"serial_test",
"sha2",
"strum",
"strum_macros",
"tokio",

View file

@ -108,7 +108,6 @@ diesel_ltree = "0.3.0"
typed-builder = "0.15.0"
serial_test = "2.0.0"
tokio = { version = "1.29.1", features = ["full"] }
sha2 = "0.10.7"
regex = "1.9.0"
once_cell = "1.18.0"
diesel-derive-newtype = "2.1.0"

View file

@ -342,9 +342,8 @@ pub async fn send_password_reset_email(
let token = uuid::Uuid::new_v4().to_string();
// Insert the row
let token2 = token.clone();
let local_user_id = user.local_user.id;
PasswordResetRequest::create_token(pool, local_user_id, &token2).await?;
PasswordResetRequest::create_token(pool, local_user_id, token.clone()).await?;
let email = &user.local_user.email.clone().expect("email");
let lang = get_interface_language(user);

View file

@ -33,7 +33,6 @@ http = { workspace = true }
futures = { workspace = true }
itertools = { workspace = true }
uuid = { workspace = true }
sha2 = { workspace = true }
async-trait = { workspace = true }
anyhow = { workspace = true }
reqwest = { workspace = true }

View file

@ -22,7 +22,6 @@ full = [
"bcrypt",
"lemmy_utils",
"activitypub_federation",
"sha2",
"regex",
"once_cell",
"serde_json",
@ -60,7 +59,6 @@ diesel-async = { workspace = true, features = [
"postgres",
"deadpool",
], optional = true }
sha2 = { workspace = true, optional = true }
regex = { workspace = true, optional = true }
once_cell = { workspace = true, optional = true }
diesel_ltree = { workspace = true, optional = true }

View file

@ -1,11 +1,6 @@
use crate::{
newtypes::LocalUserId,
schema::password_reset_request::dsl::{
local_user_id,
password_reset_request,
published,
token_encrypted,
},
schema::password_reset_request::dsl::{local_user_id, password_reset_request, published, token},
source::password_reset_request::{PasswordResetRequest, PasswordResetRequestForm},
traits::Crud,
utils::{get_conn, DbPool},
@ -17,7 +12,6 @@ use diesel::{
QueryDsl,
};
use diesel_async::RunQueryDsl;
use sha2::{Digest, Sha256};
#[async_trait]
impl Crud for PasswordResetRequest {
@ -49,29 +43,22 @@ impl PasswordResetRequest {
pub async fn create_token(
pool: &mut DbPool<'_>,
from_local_user_id: LocalUserId,
token: &str,
token_: String,
) -> Result<PasswordResetRequest, Error> {
let mut hasher = Sha256::new();
hasher.update(token);
let token_hash: String = bytes_to_hex(hasher.finalize().to_vec());
let form = PasswordResetRequestForm {
local_user_id: from_local_user_id,
token_encrypted: token_hash,
token: token_,
};
Self::create(pool, &form).await
}
pub async fn read_from_token(
pool: &mut DbPool<'_>,
token: &str,
token_: &str,
) -> Result<PasswordResetRequest, Error> {
let conn = &mut get_conn(pool).await?;
let mut hasher = Sha256::new();
hasher.update(token);
let token_hash: String = bytes_to_hex(hasher.finalize().to_vec());
password_reset_request
.filter(token_encrypted.eq(token_hash))
.filter(token.eq(token_))
.filter(published.gt(now - 1.days()))
.first::<Self>(conn)
.await
@ -91,14 +78,6 @@ impl PasswordResetRequest {
}
}
fn bytes_to_hex(bytes: Vec<u8>) -> String {
let mut str = String::new();
for byte in bytes {
str = format!("{str}{byte:02x}");
}
str
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
@ -142,17 +121,16 @@ mod tests {
let inserted_local_user = LocalUser::create(pool, &new_local_user).await.unwrap();
let token = "nope";
let token_encrypted_ = "ca3704aa0b06f5954c79ee837faa152d84d6b2d42838f0637a15eda8337dbdce";
let inserted_password_reset_request =
PasswordResetRequest::create_token(pool, inserted_local_user.id, token)
PasswordResetRequest::create_token(pool, inserted_local_user.id, token.to_string())
.await
.unwrap();
let expected_password_reset_request = PasswordResetRequest {
id: inserted_password_reset_request.id,
local_user_id: inserted_local_user.id,
token_encrypted: token_encrypted_.to_string(),
token: token.to_string(),
published: inserted_password_reset_request.published,
};

View file

@ -535,7 +535,7 @@ diesel::table! {
diesel::table! {
password_reset_request (id) {
id -> Int4,
token_encrypted -> Text,
token -> Text,
published -> Timestamp,
local_user_id -> Int4,
}

View file

@ -7,7 +7,7 @@ use crate::schema::password_reset_request;
#[cfg_attr(feature = "full", diesel(table_name = password_reset_request))]
pub struct PasswordResetRequest {
pub id: i32,
pub token_encrypted: String,
pub token: String,
pub published: chrono::NaiveDateTime,
pub local_user_id: LocalUserId,
}
@ -16,5 +16,5 @@ pub struct PasswordResetRequest {
#[cfg_attr(feature = "full", diesel(table_name = password_reset_request))]
pub struct PasswordResetRequestForm {
pub local_user_id: LocalUserId,
pub token_encrypted: String,
pub token: String,
}

View file

@ -0,0 +1 @@
alter table password_reset_request rename column token to token_encrypted;

View file

@ -0,0 +1 @@
alter table password_reset_request rename column token_encrypted to token;