-
Notifications
You must be signed in to change notification settings - Fork 11.2k
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
[5.4] Password reset tokens are now stored as bcrypt of sha256 #18570
Comments
Rolling back such change is not possible now as it'll break the token comparison in current apps and we can't introduce such breaking change. The Password reset functionality along with many other ones shipped with laravel is added as a convenient way of doing things, you can customise it as you want in your application or use it as is, so I don't think it really matters how it works since you already customize it in your application :) |
@themsaid Fair enough, may I ask why it was implemented in the first place (out of curiosity)? I can't really think of a solid reason as to why we would double hash. |
Here's the original PR: #16850 Going to close this issue then since it's not actually a bug report, feel free to ping me though :) |
Not sure why this didn't get added the the upgrade guide since this issue was created. A mention of checking for password reset tokens will fail now would be nice haha. |
I noticed this too, it broke our production application. Very bad choice to make this! |
@EmilMoe I got around it by doing this: use Illuminate\Contracts\Hashing\Hasher as HasherContract;
...
public function __construct(HasherContract $hasher)
{
$this->hasher = $hasher;
}
...
//in controller method
$reset = PasswordReset::where('email', $request->email)->firstOrFail();
if(!$this->hasher->check($request->token, $reset->token)) //token is invalid Using the hasher that the new stuff uses to validate if the token matches. Sucks, because I didn't send along the email address with the password resets but now I have to. Also due to lack of docs on customizing resets it took a bit to find out what was wrong... At least it's all good now. Still shocked it hasn't been added to the upgrade guide.... |
I am rewriting it to my own, I can't depend my production software on something that just changes like this. I think we were many who found the email from the token. I can't even see how this adds up to the security, it just seems like an irrelevant change. |
can someone explain to me how am I to use this if I have an API? If I send user the link and he opens it from a device that did not made the request, I have no way of knowing users mail without asking him for it again. Why are you forcing me to use your idea of how auth should work by making me ask user for his mail yet again? Or is there a way to use sent token to search for mail in password_resets table? |
Send the email address in your password reset link.
…On Wed, Jun 14, 2017 at 05:11 Tino ***@***.***> wrote:
can someone explain to me how am I to use this if I have an API? If I send
user the link and he opens it from a device that did not made the request,
I have no way of knowing users mail without asking him for it again. Why
are you forcing me to use your idea of how auth should work by makin me ask
user for his mail yet again? Or is there a way to user sent token to search
for mail in password_resets table?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#18570 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAbacKaHXwjbFpM28ChDmE5XTUWpqnwJks5sD6PAgaJpZM4MuMmp>
.
|
The auth scaffold is there as a convenience with sensible defaults, all are made to be easily overwritten to match your use case. |
I understand, I just think this particular decision was a bad one and will make a lot of people spend time fixing apps broken by an irrelevant change |
@zackify can you please explain the fix in a more detailed way. Our production app is in need of this fix. Thanks. |
@berkayk you just need to pull in the hasher contract to check if the token emailed out matches the encrypted token stored in the table as I mentioned above. What more do you need help with? I’d love to help you out, just don’t know what else you need. |
@zackify I am currently sending the token in
Then in my
I am unsure where to use the hasher contract. |
@berkayk you have to do exactly what I put above, inside your token check: if ($token != null){
$reset = DB::table("password_resets")->where('email', $request->email)->firstOrFail();
if(!$this->hasher->check($request->token, $reset->token)) //throw error, token invalid
$user = User::where('email', '=', $request->email)->first();
} I send along the email address in a query parameter in my reset email template, this way the user doesn't have to type that in on the reset page. |
We ran into this with a client, make sure that your DB table "password_resets" field "token" type is acceptable of all the characters, ours was set to VARCHAR at 191 characters, we set to LONGTEXT and everything worked great. Maybe this will work for you, maybe not, but worth a try. |
I make this gist, hope it works for you guys: https://gist.github.com/oxycoder/7fe6ac9708a0e1ec90c0f624d76fb8a1 |
Hi i dont have email i only have the token how can i then verify that token is valid and not expired i am using laravel 5.4. |
@usamamashkoor you can’t. You need to pass along the email in the token url. |
Thanks @zackify yes i did that and can you please tell me if this code is correct to verify if the token is valid
|
looks good to me! @usamamashkoor |
Thanks @zackify |
I wrote following $items = \DB::table('password_resets')->where('token', 'not like', '$%')->orderBy('created_at', 'desc')->get();
foreach ($items as $item) { \DB::table('password_resets')->where('login', $item->login)->update(['token' => bcrypt($item->token)]); } For better readability here the same script formatted: /* select all entries which token is not bcryted */
$items = \DB::table('password_resets')
->where('token', 'not like', '$%')
/* start with the newest one because the possibility that they used their link is bigger */
->orderBy('created_at', 'desc')
->get();
/* update them */
foreach ($items as $item) {
\DB::table('password_resets')
->where('login', $item->login)
->update([
'token' => bcrypt($item->token)
]);
} Note: I know, that script is very inefficient but it works correct and I needed a quick solutions, because my update goes live and I realized later that the tokens are invalid. |
Thanks a bunch! |
$passwordResetToken = DB::table("password_reset_tokens")->where('token', $token)->pluck('email');
|
The change:
From 5.3 to 5.4 I noticed a breaking, undocumented change on my application. The password_resets table now stores tokens as bcrypt of a sha256 token.
The 5.3 DatabaseTokenRepository is here and the 5.4 DatabaseTokenRepository is here.
The major change is the addition of $hasher which is implemented as Illuminate\Hashing\BcryptHasher.
On line 83 we call
which creates a random SHA256 token.
Then on line 85 we call
$this->getPayload($email, $token)
Which leads to line 110:
The addition here of
$this->hasher->make($token)
now bcrypts the SHA256 token in the database.So on 5.4 we generate a SHA256 token, but then we hash it again with bcrypt and store it in the database. Why? This is a breaking change for three reasons
Additionally this obfuscation could make it harder to debug in certain situations, say for example you have a user whose password reset link isn't working. How do you now go about investigating this given the log files will have a completely different url (SHA256) compared to what is in the database (bcrypt of the SHA256)?
This seems to be a completely unnecessary level of obfuscation and I can't understand why it was implemented. SHA256 itself is perfectly secure for a temporary reset password token. Instead we now have a SHA256 token which is bcrypted and then stored in the database.
Solution
I propose we go back to the way it was and keep it as a simple SHA256 token. There is no need for this second hashing and it makes extending Laravel's native password reset functionality more difficult for no benefit.
The text was updated successfully, but these errors were encountered: