Jens Segers on Feb 03 2015

Easy id obfuscation with Laravel 5

Be sure to check out my new blog post about id obfuscation with Optimus.

Obfuscating ids in urls is a great way of hiding your application's internals from your visitors. It reduces the chance for malicious users to guess urls and prevents everyone from knowing how many user accounts or articles your website has.

For PHP, there's a cool library called hashids, which generates Youtube-like obfuscated ids; it converts an integer like 347 to yr8 and back again. So instead of writing our own obfuscation logic, let's include it in our project:

composer require hashids/hashids

Since Laravel 5, there's a new interface that is being implemented by the Eloquent models, called UrlRoutable. It tells the router instance, that it has specific methods to convert the model instance to a single value that should be used in urls. This means that you are now able to pass Eloquent objects as route parameters, and Laravel will automatically convert them for you.

By default, Laravel asks the model to return the primary key, but we can simply overwrite the getRouteKey method so that the id is obfuscated:

 * Get the value of the model's route key.
 * @return mixed
public function getRouteKey()
    $hashids = new \Hashids\Hashids('MySecretSalt');

    return $hashids->encode($this->getKey());

Easy right? Now everywhere where you pass an instance of this model to a route or url, it will return the obfuscated id:

$user = User::find(123);
echo Url::route('', [$user]);

So imagine we have the following route:

Route::get('users/{user}', ['as' => '', 'user' => [email protected]']);

As of now, the controller will still receive the obfuscated user id. But we can easily decode it back by adding this route binding:

Route::bind('user', function($value, $route)
    $hashids = new Hashids\Hashids('MySecretSalt');

    return $hashids->decode($value)[0];

This tells the router, that whenever we get passed a user parameter in the url or route, we should decode it before passing it to the controller.

Last year, I wrote a blog post about how I love route model binding. So let's take it a step further and pre-fetch the model instance:

Route::bind('user', function($value, $route)
    $hashids = new Hashids\Hashids('MySecretSalt');

    $id = $hashids->decode($value)[0];

    return User::findOrFail($id);

I hope you liked this example use case of the new UrlRoutable feature in Laravel 5. If you want me to go over some more features of Laravel 5, let me know in the comments below.


KoMV-Music 4 months ago

Does this affects performance? What if I don't want to pass the model, but the ID only when generating a route?

MBriedis 8 months ago

Super helpful tip, thanks!

behnam 1 year ago

i try use this but not working for eloquent relationship !!! this return hashing id just for one model

mmeyer2k 1 year ago

Have you ever thought about using authenticated crypto for this purpose? That way tampering is impossible...

slots2 1 year ago

игровые автоматы казино новости чемпион играть без регистрации Играть В Игровые Автоматы На Деньги На Рубли казино циркус, покер пополнение счета visa Слоты Оплатить Банковской Картой интернет казино уильям хилл Игры На Деньги Онлайн В Дурака покер онлайн на реальные деньги на русском языке Казино С Моментальным Выводом Денег

vinkla 2 years ago

There is a Laravel 5 package ready to use

Well written btw!

Constantin 2 years ago

there's a laravel facace for that -

pwfraley 2 years ago

That is a very nice feature. But what do you do if you have a REST json api? You get a list of objects back (maybe a short list) and if you want to get the details of one object you have the id not the hash id? Any idea how to get around that? (Sorry I am fairly new to laravel)

Banana 2 years ago

Can you please provide example url string?

martindilling 2 years ago

Elegant and simple way to achieve that :)

LAHAXE Arnaud 2 years ago

Nice post