[wp-trac] [WordPress Trac] #32429: Password reset links should expire

WordPress Trac noreply at wordpress.org
Thu May 28 04:53:54 UTC 2015

#32429: Password reset links should expire
 Reporter:  markjaquith   |       Owner:
     Type:  defect (bug)  |      Status:  new
 Priority:  normal        |   Milestone:  4.3
Component:  Security      |     Version:
 Severity:  normal        |  Resolution:
 Keywords:  has-patch     |     Focuses:
Changes (by nacin):

 * milestone:  Awaiting Review => 4.3


 Replying to [comment:8 markjaquith]:
 > @nacin, the reason I suggested keeping the time in the link itself was
 for 100% backwards compat for anyone who is generating those links
 themselves. But it’s not a huge concern.

 I'm trying to figure out how that would help. If they're generating the
 link themselves, then they're bypassing `retrieve_password()` which
 generates the key, hashes the key, stores the key in the DB, and sends an
 email with the key. (There's a reason this function wasn't moved to `wp-
 includes/user.php` like some others — it does too much to be an API. But
 it also does everything in one place. That means that if you're generating
 the link yourself, then you're sending the email yourself, and you're
 storing something in the DB yourself.

 Including the time in the link would only help if we wanted to expire the
 link via the time as supplied, but that's easily spoofed, thus offering no
 security and only frustrating the user.

 If we wanted, we could expire all activation keys on upgrade (we've done
 it before), then allow new hashes to enter the DB without a time, as if it
 were manually entered. Then `check_password_reset_key()` could choose to
 only validate the time if it exists in the DB.

 Alternatively, we ignore manually entered hashes, thus breaking all old
 hashes, and forcing new hashes to go through `retrieve_password()` and for
 the keys (and internal time) to be confirmed with


  * As I said, we've expired all activation keys before. See [25696]. In
 the process, we changed how keys were actually stored in the DB, and no
 one complained AFAIK, so I don't think there's much of a compatibility
 issue here. (This also wasn't that long ago.)

  * We can always put a filter in place to allow non-timeboxed hashes to
 continue to work, exactly how we did it in [25696]. In fact, I would
 follow that model to a T.

 Or did I miss something obvious?

 Other things:

  * If we're going to change the string for `expiredkey` (and it looks like
 a good change), we should change it for `invalidkey` too. We should look
 at how other systems handle this message, just to ensure we're using the
 most mainstream/common language around expired keys. (This string change
 is perhaps thus better handled in a distinct ticket.)

  * password_reset_expiration should be passed DAY_IN_SECONDS.

  * Returning the string `Invalid key` from `check_password_reset_key()`
 should be fine. We do it repeatedly already, and `wp-login.php` overrides
 the string based on the `WP_Error` code.

  * The code should account for the fact that a :time might not exist in
 the old hash. It can just check for a `:` first before exploding.
 Otherwise that `list()` will generate a notice.

  * Can a colon appear in a phpass hash? Could it appear in a future or
 alternative hash? e.g. `password_hash()` or `crypt()`. As far as I know,
 no, not for the moment. However, perhaps we should consider reversing the
 fields, thus doing `list( $time, $hash ) = explode( ':' $value, 2 )`. Just
 a thought.

Ticket URL: <https://core.trac.wordpress.org/ticket/32429#comment:10>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform

More information about the wp-trac mailing list