19

I'm developing a so called AAC (Automatic Account Creator) for a game, it's basically a site with functions to create accounts, players and several more things for players. The server only supports SHA1 and plain - which is totally unsafe. I can't dive into the source code and make changes. If there's anyway to use SHA1 I would be grateful. I just read about BCrypt, it's great but I can't really change the source code to suit BCrypt. I managed to put SHA1 on registration like this:

$password = $input['password'];
$password = sha1($password);

But I simply can't login. am I doing it wrong? seems like Laravel won't let me login.

I've got get_register and post_register, also I've got get_login and post_login. Do i need to change something in the post_login to make it login or? any hints?

I'm using Laravel's php server (php artisan serve) and phpMyAdmin on WAMP. I think Laravel checks when you are checking the DB via the Auth::attempt method laravel is doing some form of hashing to check the current pw and the logged in one to check against each other.

dynamitem
  • 1,647
  • 6
  • 25
  • 45

6 Answers6

42

You'll have to rewrite the Hash module. Thanks to Laravel's ideas of following IoC and Dependency Injection concepts, it'll be relatively easy.

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

"autoload": {
    "classmap": [
        // ...

        "app/libraries"
    ]
},

Now, it's time we create our class. Create a SHAHasher class, implementing Illuminate\Hashing\HasherInterface. We'll need to implement its 3 methods: make, check and needsRehash.

Note: On Laravel 5, implement Illuminate/Contracts/Hashing/Hasher instead of Illuminate\Hashing\HasherInterface.

app/libraries/SHAHasher.php

class SHAHasher implements Illuminate\Hashing\HasherInterface {

    /**
     * Hash the given value.
     *
     * @param  string  $value
     * @return array   $options
     * @return string
     */
    public function make($value, array $options = array()) {
        return hash('sha1', $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;
    }

}

Now that we have our class done, we want it to be used by default, by Laravel. To do so, we'll create SHAHashServiceProvider, extending Illuminate\Support\ServiceProvider, and register it as the hash component:

app/libraries/SHAHashServiceProvider.php

class SHAHashServiceProvider extends Illuminate\Support\ServiceProvider {

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

    }

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

}

Cool, now all we have to do is make sure our app loads the correct service provider. On app/config/app.php, under providers, remove the following line:

'Illuminate\Hashing\HashServiceProvider',

Then, add this one:

'SHAHashServiceProvider',
rmobis
  • 26,129
  • 8
  • 64
  • 65
  • Thanks buddy, I just wanted to ask you how do I use it now? Do I use the standard Hash::make function? EDIT- it returns an error because I deleted 'Illuminate\Hashing\HashServiceProvider', under providers and added 'SHAHashServiceProvider`: http://3.imgland.net/C8Kem.png Here's how I changed the provider: http://1.imgland.net/zEongM.png – dynamitem Jul 18 '13 at 14:03
  • Ooops, my bad. I made a typo on my post, when adding the new service provider. It's 'SHAHashServiceProvider', not 'SHAHashServiceProvider`. (Note the last apostrophe) – rmobis Jul 19 '13 at 02:39
  • How's your composer.json? – rmobis Jul 19 '13 at 18:34
  • And is your file correctly name, and under the `app/libraries` folder? – rmobis Jul 19 '13 at 22:19
  • Well, this is weird, it's not finding the class. Something to do with the autoloader... – rmobis Jul 21 '13 at 05:18
  • I'll try with a fresh version of Laravel 4. – dynamitem Jul 21 '13 at 14:00
  • ok, got it working, I had to transfer all the files into a fresh Laravel 4 installation, and I had to composer dump-autoload. Now I assume it's working, but, I get an error on line 11: http://4.imgland.net/BvSKhs.png It says undefined variable.. – dynamitem Jul 21 '13 at 14:52
  • Oops, my mistake again. Was supposed to be `$this->app`, not `$this->$app`. – rmobis Jul 22 '13 at 01:19
  • @Raphael_ Thanks for this help. I followed every step except my library name is `app/lib` But I am still getting error at `php artisan dump-autoload` `Class 'SHAHashServiceProvider' not found` Here are links to review my work. http://1.imgland.net/y1bPOl.png http://2.imgland.net/k2rqkg.png http://3.imgland.net/GMtqwb.png http://2.imgland.net/zbkv1W.png – SAM Feb 28 '14 at 14:04
  • 1
    It's `composer dump-autoload` – rmobis Feb 28 '14 at 14:37
  • btw don't forget to concatenate the same salt to the password before hashing (if you are using salt in CakePHP). – lgomezma Jun 12 '14 at 11:37
  • @Igomezma thanks for pointing that out, will update the answer with a note. – rmobis Jun 12 '14 at 13:24
  • For Laravel 5 the body of the `register` method in the service provider should look like this: `$this->app->singleton('hash', function() { return new ShaHasher(); });` – Rico Leuthold Feb 06 '15 at 16:30
  • @RicoLeuthold In Laravel 5 I am getting FatalErrorException in Shahasher.php (service) line 7:Interface 'Illuminate\Hashing\HasherInterface' not found – Kiren S Apr 24 '15 at 06:25
  • @Raphael_ In Larvel 5, If I am correct, don't need to create app/libraries folder. Instead I wrote Shahashser.php in app/services and ShahashserServiceProvider.php in app/providers is this correct? But I am getting the above error – Kiren S Apr 24 '15 at 06:30
  • @KirenSiva In Laravel 5, the interface is now called `Illuminate/Contracts/Hashing/Hasher`. I'll update the post to warn about that. – rmobis Apr 24 '15 at 14:04
  • Hmmm I got it after a long research :) – Kiren S Apr 24 '15 at 14:16
  • Latino: Sirve igual para 5.1 en adelante – Alejo Florez Aug 15 '17 at 19:06
  • Thanks! Sorry to ask this question, I'm new to Laravel. I just don't understand why this works. By just adding our custom provider to the providers array and removing the original one, how does Laravel know to use our provider for hashing and not throw an error it 'can't find HashServiceProvider'? Understanding this would help me understand Laravel better, help would be appreciated :) – Samuël Visser Apr 12 '19 at 08:32
  • @SamuëlVisser it know because de declared inside the `provides` function that it provides the `'hash'` service. So it knows that our provider is the one providing this specific service. – rmobis Apr 13 '19 at 19:21
8

It took me a lot of time to get something similar happening in Laravel 5.6 but this thread was invaluable. The accepted answer gets you very close but there are still some ambushes along the way (as can be seen in the comments) so instead of struggling with the comments I thought it would be helpful for others to have it presented as an answer.

In my case I needed to access an existing database and couldn't change the user file. The passwords were saved in SHA256 format with a hash key applied as well. So the objective for me was to really only get the check function working.

I'm really new to Laravel and I know there will be a better way around this issue but I couldn't get the app\Libraries area to register so I put both SHAHasher.php and SHAHashServiceProvider.php into app\Providers which I would assume is some sort of Laravel sacrilege but it was the only way I got it to work. :)

The steps I took (hijacking rmobis's excellent answer for Laravel 4) was:

The hash key used in the original app needed to be accessed by Laravel so I added this to the bottom of .env.

.env

...
HASH_KEY=0123_key_code_added_here_xyz

app/Providers/SHAHasher.php

namespace App\Providers;

use Illuminate\Contracts\Hashing\Hasher;

class SHAHasher implements Hasher
{

  /**
   * Get information about the given hashed value.
   * TODO: This was added to stop the abstract method error.
   *
   * @param  string  $hashedValue
   * @return array
   */
  public function info($hashedValue)
  {
    return password_get_info($hashedValue);
  }

  /**
   * Hash the given value.
   *
   * @param  string $value
   * @return array   $options
   * @return string
   */
  public function make($value, array $options = array())
  {
    // return hash('sha1', $value);
    // Add salt and run as SHA256
    return hash_hmac('sha256', $value, env('HASH_KEY'));
  }

  /**
   * 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;
  }

}

app/Providers/SHAHashServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class SHAHashServiceProvider extends ServiceProvider {

  /**
   * Register the service provider.
   *
   * @return void
   */
  public function register() {
    $this->app->singleton('hash', function() {
      return new SHAHasher();
    });
  }

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

}

app/config/app.php

remove or comment out // Illuminate\Hashing\HashServiceProvider::class,

Add App\Providers\SHAHashServiceProvider::class,

I didn't need to register the users (only to allow them to use their existing logins to get in) so I only tested it for access. I'm not sure why the app/Libraries area wouldn't take. I was getting an error

Class 'SHAHashServiceProvider' not found

when I ran the composer dump-autoload command until I moved both into app/Providers.

Hope this helps others trying to get the anser to work in Laravel 5.

Das123
  • 855
  • 3
  • 14
  • 26
5

There is actually a easier (or more simple, at least) solution for a case like this. you can 'fake' the hashing, by using this method in the user model:

public function getAuthPassword() {
    return Hash::make($this->password);
}

And hashing the input with your own hash function. For instance, if your passwords are currently hashed with sha1, you can validate the user with

Auth::attempt(array('email' => $email, 'password' => sha1($password))

It doesn't feel like good coding practice, to do it this way, but it will certainly be easier than rewriting the hash module.

Benubird
  • 18,551
  • 27
  • 90
  • 141
  • Looks good to me. I think the best method is to "secretly" replace the old hashed passwords with correctly protected passwords. (see [this](http://stackoverflow.com/questions/19878319/laravel-4-auth-use-md5-instead-of-the-integrated-hashmake)) – AturSams Jun 10 '14 at 10:53
  • Really nice idea, but This one doesn't work as Hash:: facade encrypts the user password when trying to validate against the password in DB. So it's better to use the ServiceProvider implementation above to handle md5 hashs. It's better to use bcrypt or sha1 instead of md5 but in some projects that's not an option =( – trm42 Jul 15 '14 at 13:31
  • 3
    @trm42 Actually it does work. It compares the incoming password X against the db password Y using hash(X)==Y, since it assumes that Y is already hashed correctly. What we are doing, is setting X to match Y, then hashing Y so that it will match X. The actual test being run then, is hash(sha1(X))==hash(sha1(Y)). Have a look at the source code for the guard class, you'll see how it works. – Benubird Jul 15 '14 at 13:38
  • @Benubird Good point. Must my my day as a blind code squirrel, sorry about that. For some reason this approach didn't work for me when testing. – trm42 Jul 15 '14 at 14:25
3

The Laravel 7 way

In Laravel 7 adding new hash methods to Laravel got a lot easier you can also still support the old Hash methods instead of basically overwriting them.

First we will create a subfolder in the app folder called Libs or Libaries or basically whatever you want to call it. In that folder I created a folder called CustomHash where I store all my custom hashers.

Through PSR-4 it is automatically detected and we do not need to add it anywhere.

The Namespace is based on the folder names I chose.

app/AppServiceProvider.php

First we use the namespace of the Sha1Hasher

use App\Libs\CustomHash\Sha1Hasher;

then in the boot() function of your AppServiceProvide.php

        Hash::extend("sha1", function($app)
        {
            return new Sha1Hasher();
        });

app/Libs/CustomHash/Sha1Hasher.php

<?php

    namespace App\Libs\CustomHash;

    use Illuminate\Contracts\Hashing\Hasher as HasherContract;
    use Illuminate\Hashing\AbstractHasher;

    class Sha1Hasher extends AbstractHasher implements HasherContract {

        /**
         * Hash the given value.
         *
         * @param  string  $value
         * @return array   $options
         * @return string
         */
        public function make($value, array $options = array()) {
            //I have custom encoding / encryption here//
            //Define your custom hashing logic here//
            return sha1($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;
        }

    }



And that's basically it if you want to change the default hash algorithm for everything there is one more thing to do:

config/hashing.php

Change the default driver for hashing to the sha1 driver we implemented with the edits above.

    'driver' => 'sha1',

Noff
  • 31
  • 3
1

You can do this


dd(
sha1(55),
hash('sha1', 55),
hash_hmac('sha1', 55, env('APP_KEY')),
hash_hmac('sha256', 55, env('APP_KEY')),
hash_hmac('sha512', 55, env('APP_KEY')),
);

// outputs:
/*
"8effee409c625e1a2d8f5033631840e6ce1dcb64"
"8effee409c625e1a2d8f5033631840e6ce1dcb64"
"a2ebb15e6747d1c09f2754787ab390d35c24996e"
"f0fadaa9fdd518947ac3f698196d5370dc2409bbdfbe9e37bd30f935b6cc1f47"
"c1e6f4e144565aa4fdb9c7ae34aba7d43424e20fa40ad3a0641d20bfbb3b9681ded4f4cc8b4661804e4a753118a3f984585d6915ee6d4b75a95310af48afe920"
*/

PHANTOM-X
  • 502
  • 6
  • 16
-1

Laravel 7 UPD

same as Das123 answer but a little fix to

app/Providers/SHAHashServiceProvider.php

namespace App\Libraries\ShaHash;

use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Hashing\AbstractHasher;

class SHAHasher extends AbstractHasher implements HasherContract

  /**
   * Register the service provider.
   *
   * @return void
   */
  public function register() {
    $this->app->singleton('hash', function() {
      return new SHAHasher();
    });
  }

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

}