7

I'm porting over a legacy app into Laravel. The old app used MD5 to hash the passwords without a salt, so I need to replicate that within Laravel. For the record, we are changing the passwords to bcrypt with a salt, but it's not a simple process and requires a user login to do so - for the meantime I just need to get logins working with the legacy hashes.

I have followed this guide to convert Auth::hash to MD5: How to use SHA1 encryption instead of BCrypt in Laravel 4?

When I print out the password in plain text and the generated hash in my make method when registering an account:

public function make($value, array $options = array()) {
    echo $value.'<br>'.hash('md5', $value);
    exit;
    return hash('md5', $value);
}

I get the following:

123456
e10adc3949ba59abbe56e057f20f883e

Great, that's what I need. However, when that is saved to the database I get a different hash entirely. My guess is that Laravel is salting the password elsewhere, but I can't find where and how to override this.

My MD5Hasher.php file inside app/libraries:

<?php
class MD5Hasher implements Illuminate\Contracts\Hashing\Hasher {

    /**
     * Hash the given value.
     *
     * @param  string  $value
     * @return array   $options
     * @return string
     */
    public function make($value, array $options = array()) {
        return hash('md5', $value);
    }

    /**
     * Check the given plain value against a hash.
     *
     * @param  string  $value
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function check($value, $hashedValue, array $options = array()) {
        return $this->make($value) === $hashedValue;
    }

    /**
     * Check if the given hash has been hashed using the given options.
     *
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function needsRehash($hashedValue, array $options = array()) {
        return false;
    }

}

My MD5HashServiceProvider.php:

<?php
class MD5HashServiceProvider extends Illuminate\Support\ServiceProvider {

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register() {
        $this->app['hash'] = $this->app->share(function () {
            return new MD5Hasher();
        });

    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides() {
        return array('hash');
    }

}

My AuthController.php looks like the following:

<?php

namespace App\Http\Controllers\Auth;

use Hash;
use App\User;
use Validator;
use Mail;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

class AuthController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Registration & Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users, as well as the
    | authentication of existing users. By default, this controller uses
    | a simple trait to add these behaviors. Why don't you explore it?
    |
    */

    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    //protected $redirectTo = '/account';

    /**
     * Create a new authentication controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest', ['except' => 'getLogout']);
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|confirmed|min:6',
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return User
     */
    protected function create(array $data)
    {
        $this->redirectTo = '/register/step-1';

        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);

        // email the user
        Mail::send('emails.register', ['user' => $user], function($message) use ($user)
        {
            $message->to($user->email, $user->name)->subject('Edexus - Welcome');
        });

        // email the admin
        Mail::send('emails.register-admin', ['user' => $user], function($message) use ($user)
        {
            $message->to('admins@***.com', 'Edexus')->subject('Edexus - New user sign up');
        });

        return $user;
    }
}
Community
  • 1
  • 1
Mike
  • 8,767
  • 8
  • 49
  • 103
  • 2
    @aldrin27 - thanks for the insightful comment. I'm not using SHA1, I'm using plain MD5 (which is even worse), but it's part of a migration process to salted bcrypt. – Mike Nov 06 '15 at 08:26
  • The passwords can be hashed either in User Model or in the AuthController and the relevant traits. You need to look there for the extra hashing or please provide the files to be able to help. – Mina Abadir Nov 06 '15 at 08:36
  • @MinaYoussef I have added my AuthController.php to the question. I've `dd` the `Hash::make` at point of calling `User::create()` and the hash is different to that being saved. – Mike Nov 06 '15 at 08:44
  • @mikemike sorry for that. LOL. I thought you would like to use SHA1 because of this -> How to use SHA1 encryption instead of BCrypt in Laravel 4? – aldrin27 Nov 06 '15 at 08:57
  • Are you using the standard User model? – Mina Abadir Nov 06 '15 at 11:04
  • Also to take the Controller out of equation, would you please try to save a User through artisan tinker. After that, please check the hash is correct or wrong. If correct, then we focus on Controller and traits, otherwise it's the model. – Mina Abadir Nov 06 '15 at 11:06
  • @MinaYoussef Tinker does the same thing when doing `User::create()` - ie. generating a different hash than expected. I'm using the standard model, with a slight change. I'll add to question now – Mike Nov 06 '15 at 11:31
  • @MinaYoussef yep - just realised. My Model has a `public function setPasswordAttribute($value)` method which hashes (it's used for my admin panel). – Mike Nov 06 '15 at 11:35
  • @MinaYoussef Can you pop it in an answer and I'll mark it correct? – Mike Nov 06 '15 at 11:35
  • Great to hear, I added my personal recommendation as well in the answer. – Mina Abadir Nov 06 '15 at 11:38

2 Answers2

4

Check out the password mutator in your User Model. It's hashing the password another time after hashing it in the controller.

My recommendation is hash the password once in your creating() and updating() model events, and remove it from the mutator and controller.

Mina Abadir
  • 2,951
  • 2
  • 15
  • 20
2

step1: create app/libraries folder and add it to composer's autoload.classmap

"autoload": {
    "classmap": [
        // ...
        "app/libraries"
    ]
},

step 2: create two php file MD5Hasher.php and MD5HashServiceProvider in app/libraries MD5Hasher.php

<?php
namespace App\Libraries;
use Illuminate\Contracts\Hashing\Hasher;
class MD5Hasher implements Hasher {
    /**
     * Hash the given value.
     *
     * @param  string  $value
     * @return array   $options
     * @return string
     */
    public function make($value, array $options = array()) {
        return md5($value);
    }
    /**
     * Check the given plain value against a hash.
     *
     * @param  string  $value
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function check($value, $hashedValue, array $options = array()) {
        return $this->make($value) === $hashedValue;
    }
    /**
     * Check if the given hash has been hashed using the given options.
     *
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function needsRehash($hashedValue, array $options = array()) {
        return false;
    }
}

MD5HashServiceProvider.php

<?php
namespace App\Libraries;
use Illuminate\Support\ServiceProvider;
class MD5HashServiceProvider extends ServiceProvider {
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register() {
//        $this->app['hash'] = $this->app->share(function () {
//            return new MD5Hasher();
//        });
        $this->app->singleton('hash', function () {
            return new MD5Hasher();
        });
    }
    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides() {
        return array('hash');
    }

step3: Hide or Remove "Illuminate\Hashing\HashServiceProvider::class" in config/app.php and add "App\Libraries\MD5HashServiceProvider::class"