Enmascaramiento del ID en la URL mediante hashids en Laravel

  • Publicado el 03 marzo, 2025
  • Palabras: 815

Genera identificadores únicos cortos a partir de números en tu aplicación Laravel. Estos identificadores son compatibles con URL, pueden codificar varios números y no contienen palabras vulgares comunes.

Enmascaramiento del ID en la URL mediante hashids en Laravel

De forma predeterminada, una URL generada por una aplicación Laravel contendrá el ID de un modelo como este https://app.test/users/1 donde 1 es el ID del elemento. A menudo, esto está bien, pero a veces es posible que desees ocultarlo (u ofuscarlo). Los dos casos principales para hacer esto pueden ser:

 

  • Seguridad: si muestra el ID de todos sus modelos, un usuario malintencionado podría usarlo para su beneficio.
  • Más profesional: al enmascarar el ID, ocultas la cantidad de elementos en la base de datos. La cantidad de usuarios que hay en tu aplicación, por ejemplo, podría indicar cuán popular es tu aplicación. Si puedo ver que el último usuario que se registró tiene el ID 5, es probable que tu aplicación aún no sea muy popular.

 

Lo que queremos lograr es una URL donde el ID esté enmascarado de la manera https://app.test/users/RqB3N. Puedes hacer esto con una librería llamada hashids y un poco de conocimiento de Laravel.

 

#Instalación de dependencias

Hay muchos forks de la biblioteca hashids y, por suerte, hay uno para PHP. Incluso hay un contenedor para Laravel, que es el que vamos a utilizar. Vamos a instalarlo mediante Composer:

 

composer require vinkla/hashids

 

A continuación debemos agregar el facade a nuestra lista de facades. En Laravel 11, se ha simplificado el método de registro de facades para fomentar el uso directo de nombres de clase totalmente calificados, lo que se ajusta más a las prácticas modernas de PHP. Sin embargo, si aún deseas utilizar alias para sus facades, puedes registrarlas en los services providers de tu aplicación.

 

Creamos un service provider:

 

php artisan make:provider AliasServiceProvider

 

En el service provider, dentro del método de register, utiliza AliasLoader para registrar tus alias:

 

<?php

namespace AppProviders;

use IlluminateFoundationAliasLoader;
use IlluminateSupportServiceProvider;

class AliasServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     */
    public function register(): void
    {
        // Get the AliasLoader instance
        $loader = AliasLoader::getInstance();

        // Add your aliases
        $loader->alias('Hashids', VinklaHashidsFacadesHashids::class);
    }

    /**
     * Bootstrap services.
     */
    public function boot(): void
    {
        //
    }
}

 

Para finalizar, publicaremos el fichero de configuración que modificaremos en breve:

 

php artisan vendor:publish --provider="VinklaHashidsHashidsServiceProvider"

 

#Modelos, controladores y routing

En este punto, supongo que ya tienes configurados tus modelos, controladores y rutas. No hay nada especial en los controladores ni en los modelos (aparte del trait que agregaremos más adelante). Si aún no lo has hecho, puedes continuar y configurarlos ahora.

En tu fichero de rutas, debe asegurarse de utilizar route model binding:

 

use AppModelsUser;

Route::get('/users/{user}', function (User $user) {
    return $user->email;
});

 

#Estableciendo conexiones

En nuestra configuración, que se encuentra en config/hashids.php, necesitamos agregar una conexión para cada uno de los modelos en los que nos gustaría usar hashids. Al hacer esto, podemos establecer un salt diferente para cada conexión, lo que significa que el hash generado para el Usuario con el ID de 1 es diferente del Producto con el ID de 1.

Si usamos la misma conexión para ambos, ambos tendrán el mismo hashid. Si bien no hay ningún problema técnico con que ambos tengan el mismo hashid, aún podemos estar revelando información sobre la aplicación si puede inferir cuál podría ser uno de los identificadores subyacentes.

El salt puede ser cualquier cosa que desee, siempre que sea única para cada elemento. Para simplificar las cosas, tiendo a usar el nombre del modelo más un hash MD5 aleatorio.

También podemos ajustar la longitud inicial del hashid en este punto.

 

'connections' => [
    App\Models\User::class => [
        'salt' => App\Models\User::class.'7623e9b0009feff8e024a689d6ef59ce',
        'length' => 8,
    ],
],

 

#Estableciendo el route key para nuestros modelos

Con el route model binding podemos personalizar la clave que se utiliza anulando el método getRouteKey en el modelo. De forma predeterminada, este es el ID de nuestro modelo, pero en realidad queremos que sea el hash del ID del elemento codificado por la biblioteca hashids. Como ya sabemos que vamos a utilizar en el modelo User y seguramente nos sirva para más modelos, tiene sentido abstraerlo y colocarlo en un trait.

Cree un nuevo archivo en app/Traits llamado Hashidable.php con el siguiente contenido:

 

<?php

namespace AppTraits;

trait Hashidable
{
    public function getRouteKey()
    {
        return Hashids::connection(get_called_class())->encode($this->getKey());
    }
}

 

Ahora necesitamos usar este trait en nuestros modelos. En app/Models/User.php, asegúrese de importar el trait y usarlo:

 

<?php

namespace AppModels;

use AppTraitsHashidable;

class User extends Authenticatable
{
    use Hashidable;

   //

 

No olvides hacer esto también para el resto de modelos que usen esta característica

En este punto, cualquier URL que nuestra aplicación genere para nuestro modelo User contendrá el hashid en lugar del ID del modelo. Perfecto. ¡Pero hay más! Necesitamos indicarle a Laravel qué hacer con el hashid cuando lo vea en la URL. En este momento, intentará buscar el hashid en la columna ID de la base de datos, pero primero debemos decodificarlo.

 

#Descodificación y binding

Lógicamente, lo que queremos hacer en este punto es decodificar el hashid de nuevo al ID del modelo y luego devolver la instancia del modelo. Podemos hacer exactamente esto en app/Providers/HashidsServiceProvider.php. En el método boot podemos indicarle a Laravel qué hacer cuando encuentre una instancia de un modelo en particular con respecto al enrutamiento.

 

php artisan make:provider HashidsServiceProvider

 

Dentro del service provider:

 

public function boot(): void
{
    Route::bind('user', function ($value, $route) {
        return $this->getModel(AppModelsUser::class, $value);
    });
}

private function getModel($model, $routeKey)
{
    $id = Hashids::connection($model)->decode($routeKey)[0] ?? null;
    $modelInstance = resolve($model);

    return  $modelInstance->findOrFail($id);
}

 

Cuando encontramos una instancia de User, llamamos a un método reutilizable getModel. Aquí aceptamos 2 parámetros: el modelo y la clave de ruta. Podemos convertir la clave de ruta nuevamente en el ID decodificándola con la conexión de hashids para el modelo. Finalmente, devolvemos la instancia del modelo.

 

¡Eso es todo! Ahora, si visita la URL https://app.test/users/RqB3N, verá el usuario con el ID 1. ¡Genial!

Si desea agregar más modelos, debe crear una nueva conexión en config/hashids.php, agregar el trait hashidable a tu modelo y crear un nuevo enlace en el método de arranque para el modelo en app/Providers/HashidsServiceProvider.php.

 
 
Antonio Jenaro
Antonio Jenaro

Web Developer

Archivado en:

Inicia la conversación

Hazte miembro de Antonio Jenaro para comenzar a comentar.

Regístrate ahora

¿Ya estás registrado? Inicia sesión