-
-
Notifications
You must be signed in to change notification settings - Fork 884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expire password reset tokens when used (fixes #3270). #3388
Expire password reset tokens when used (fixes #3270). #3388
Conversation
@@ -10,11 +10,13 @@ pub struct PasswordResetRequest { | |||
pub token_encrypted: String, | |||
pub published: chrono::NaiveDateTime, | |||
pub local_user_id: LocalUserId, | |||
pub expires_at: chrono::NaiveDateTime, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont see any reason to add a new column here. Would be enough to delete the row when the password reset was performed successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that deleting rows would enable malicious actors to bypass the daily limit I added in #3344
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What @sunaurus said, I don't see any real harm in adding the extra column. If expiration duration ever becomes configurable it might be nice to be able to tell when the actual expiration time was, vs having to figure out what the expiration duration was configured to at the time and add it to the published timestamp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense about the daily limit. But for that a boolean column expired
would be enough, I dont see any reason for a second timestamp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could change it to bool if you like, I just figured storing when it expired might be useful information in some case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I suppose a bool would work fine, I've just always seen it implemented with a timestamp so that's what I went with 🤷 I'll change it later when I have time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also add a daily job to delete these based on their published time > than a day, otherwise the password reset request table will grow pointlessly large.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just got some time to work on this again and I realized why I'm not so much a fan of a boolean column. If we go with "expired" as the name then it's confusing because you'll end up with rows that are expired when the expired column is still false. If we go with "used" we'll be marking rows as used when they weren't actually used, they're just no longer valid. Is there a way to name this that's not confusing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the column be marked as used
before its been used? It should be set to true after its been used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be marked used
if a new token is generated before 24 hours. Maybe it's not too big of a deal and I'm overthinking it, I'll get it updated this weekend.
baf80cf
to
c82f2d3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with nutomic that a simple used
boolean column would be good.
Another thing: Why are we pointlessly converting the token back and forth to from bytes to hex? What would be wrong about just using an autogenerated uuid
column in postgres, emailing them that, and checking it?
@@ -0,0 +1,2 @@ | |||
alter table password_reset_request add column expires_at timestamp not null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a not null column without a default means that any existing password_reset_request rows will result in a broken migration. I suggest either deleting them all before running this, or adding a default.
@@ -0,0 +1,2 @@ | |||
alter table password_reset_request add column expires_at timestamp not null; | |||
create index idx_password_reset_request_token_encrypted on password_reset_request using hash (token_encrypted); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using hash? Why not just a regular index?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another thing: why not just generate the UUID in postgres, instead of rust?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using hash? Why not just a regular index?
Btree index seems a waste since ordering by hashed tokens doesn't seem useful in any way, hash index is more performant for direct access.
Another thing: why not just generate the UUID in postgres, instead of rust?
A couple of reasons, one is so that it can be hashed before inserting, and another is that it's not clear if postgres's UUID4 generation is guaranteed to be cryptographically secure.
@@ -10,11 +10,13 @@ pub struct PasswordResetRequest { | |||
pub token_encrypted: String, | |||
pub published: chrono::NaiveDateTime, | |||
pub local_user_id: LocalUserId, | |||
pub expires_at: chrono::NaiveDateTime, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also add a daily job to delete these based on their published time > than a day, otherwise the password reset request table will grow pointlessly large.
@@ -33,11 +33,16 @@ impl Perform for PasswordChangeAfterReset { | |||
return Err(LemmyError::from_message("passwords_dont_match")); | |||
} | |||
|
|||
// Expire reset token | |||
// TODO do this in a transaction along with the user update (blocked by https://github.com/LemmyNet/lemmy/issues/1161) | |||
PasswordResetRequest::expire(context.pool(), reset_request.id).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Transactions should be doable now that #3420 is merged.
Added an expires_at column for password reset requests that gets set to a few seconds in the past whenever a new password reset request is created for a user, or when it's used.
I also added a hash index on the hashed token since that's primarily what's going to be queried and a full table scan isn't great once the table gets larger.