From 2d0f77af590de70d228a1bdbaab0c937e596c090 Mon Sep 17 00:00:00 2001
From: Nutomic <me@nutomic.com>
Date: Wed, 2 Aug 2023 19:02:53 +0200
Subject: [PATCH] Dont use sha hash for password reset token (fixes #3491)
 (#3795)

---
 Cargo.lock                                    |  2 --
 Cargo.toml                                    |  1 -
 crates/api_common/src/utils.rs                |  3 +-
 crates/apub/Cargo.toml                        |  1 -
 crates/db_schema/Cargo.toml                   |  2 --
 .../src/impls/password_reset_request.rs       | 36 ++++---------------
 crates/db_schema/src/schema.rs                |  2 +-
 .../src/source/password_reset_request.rs      |  4 +--
 .../down.sql                                  |  1 +
 .../up.sql                                    |  1 +
 10 files changed, 13 insertions(+), 40 deletions(-)
 create mode 100644 migrations/2023-08-02-144930_password-reset-token/down.sql
 create mode 100644 migrations/2023-08-02-144930_password-reset-token/up.sql

diff --git a/Cargo.lock b/Cargo.lock
index bac96d5e..98a89095 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
diff --git a/Cargo.toml b/Cargo.toml
index 405ed9d4..424c10e7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs
index 5a678191..6bdc3028 100644
--- a/crates/api_common/src/utils.rs
+++ b/crates/api_common/src/utils.rs
@@ -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);
diff --git a/crates/apub/Cargo.toml b/crates/apub/Cargo.toml
index cdddea4f..ebdf3245 100644
--- a/crates/apub/Cargo.toml
+++ b/crates/apub/Cargo.toml
@@ -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 }
diff --git a/crates/db_schema/Cargo.toml b/crates/db_schema/Cargo.toml
index a5d3e21f..3370a559 100644
--- a/crates/db_schema/Cargo.toml
+++ b/crates/db_schema/Cargo.toml
@@ -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 }
diff --git a/crates/db_schema/src/impls/password_reset_request.rs b/crates/db_schema/src/impls/password_reset_request.rs
index 9daaa166..a5a8fc49 100644
--- a/crates/db_schema/src/impls/password_reset_request.rs
+++ b/crates/db_schema/src/impls/password_reset_request.rs
@@ -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,
     };
 
diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs
index d6e2cf69..7106b1c9 100644
--- a/crates/db_schema/src/schema.rs
+++ b/crates/db_schema/src/schema.rs
@@ -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,
     }
diff --git a/crates/db_schema/src/source/password_reset_request.rs b/crates/db_schema/src/source/password_reset_request.rs
index 6c28c649..6e1b572d 100644
--- a/crates/db_schema/src/source/password_reset_request.rs
+++ b/crates/db_schema/src/source/password_reset_request.rs
@@ -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,
 }
diff --git a/migrations/2023-08-02-144930_password-reset-token/down.sql b/migrations/2023-08-02-144930_password-reset-token/down.sql
new file mode 100644
index 00000000..5f938da2
--- /dev/null
+++ b/migrations/2023-08-02-144930_password-reset-token/down.sql
@@ -0,0 +1 @@
+alter table password_reset_request rename column token to token_encrypted;
diff --git a/migrations/2023-08-02-144930_password-reset-token/up.sql b/migrations/2023-08-02-144930_password-reset-token/up.sql
new file mode 100644
index 00000000..0bdd1655
--- /dev/null
+++ b/migrations/2023-08-02-144930_password-reset-token/up.sql
@@ -0,0 +1 @@
+alter table password_reset_request rename column token_encrypted to token;
-- 
2.44.1