Laravel es uno de los frameworks de PHP mas utilizados en la actualidad, y aunque recibe actualizaciones constantemente añadiendo nuevas funcionalidades, hay algunos procesos que aún no incorpora. Uno de ellos es la encriptación automática de ids.
En primer lugar, tenemos que conocer una serie de factores que serán necesario tener claros a la hora de encriptar.
- APP_KEY: laravel encripta los datos usando una key que se encuentra en el archivo .env. Esta clave se genera automáticamente con la instalación y se puede cambiar usando el comando php artisan key:generate, lo cual es recomendable hacer cada cierto tiempo para hacer más segura nuestra página.
- Helpers: funciones de ayuda o que realizan funciones específicas.
- Traits: emula la herencia múltiple en php y se usa cuando se va a usar código en varios lugares de nuestra aplicación.
- Accesors: da un valor a un atributo del modelo.
- Appends: añade un nuevo atributo al modelo.
- Middlewares: filtra las peticiones php
Creación del helper
Desde aquí generamos un código alfanumérico a través de la encriptación de un id numérico. Crearemos el helper en app/Helpers y lo llamaremos Encryptor.php
class Encryptor { public static function method() { return Config::get('app.cipher'); } public static function hash_key(){ return hash('sha256', Str::substr(Config::get('app.key'), 7)); } public static function iv() { $secret_iv = Str::substr(Config::get('app.key'), 7); $iv = substr(hash('sha256', $secret_iv), 0, 16); return $iv; } public static function encrypt($value) { $output = openssl_encrypt($value, self::method(), self::hash_key(), 0, self::iv()); $output = base64_encode($output); return $output; } public static function decrypt($value) { $output = openssl_decrypt(base64_decode($value), self::method(), self::hash_key(), 0, self::iv()); return (int)$output; } }
Las tres primeras funciones se usarán para generar los datos que luego usaremos encriptar el código en las funciones de encrypt y decrypt.
Función method: Recogemos el método de encriptación declarado en el fichero app.php. Por defecto laravel usa AES-256-CBC
Función hash_key: encriptamos el código del APP_KEY que encontramos en el fichero .env con el método sha256 (se puede usar otro método)
Función iv: generamos el valor que garantiza que el valor encriptado sea único. Aquí podemos usar cualquier valor. Nosotros encriptaremos una parte del APP_KEY y la usaremos como iv.
Funciones encrypt y decrypt: Estas funciones recibirán un valor (el id en este caso) el cual encriptaremos o desencriptaremos usando las funciones php openssl_encrypt/openssl_decrypt y base64_encode/base64_decode respectivamente, usando los valores generados por las funciones anteriores.
Creación del helper
En el trait crearemos el accesor para luego poder hacer un use en cada modelo que necesitemos encriptar un id. Crearemos el helper en app/Trait y lo llamaremos EncryptationId.php
El accesor hará uso de la función encrypt que hemos creado en el helper Encryptor.php
use App\Helpers\Encryptor; trait EncryptationId { /** * Funcion que recupera el id del modelo y lo encripta * @param $value valor del id autonumerico * @return string con el valor del id encriptado */ public function getEncidAttribute() { return Encryptor::encrypt($this->attributes['id']); } }
Modelo
Hacemos un use del trait que contiene el accesor y añadimos la variable encid que contendrá el id encriptado al modelo.
use App\Traits\EncryptationId; class User extends Model { use EncryptationId; protected $appends = ['encid']; }
Controlador
Con hacer uso del modelo para generar un objeto, ya podremos obtener el encid
public function index() { $users = $User::all(); return view(‘users’, compact(users)); } public function edit($id) { $user = $User::find($id); //dd($user->encid) return view(‘user’, compact(user)); } public function store($id, Request $request) { $user = $User::find($id); $user->update([ //$request... ]) return view(‘users’); }
Vista
En la vista sólo tendremos que hacer uso del objeto que hemos enviado y extraer el encid para usarlo donde queramos.
users.blade.php @foreach($users as $user) Editar usuario: <a href=”/users/{{$user->encid}}”>{{$user->name}} </a> @endforeach user.blade.php <form method="post" action="/user/{{$user->encid}}"> ….. </form>
Middleware
Añadiremos el código que se encargará de comprobar que lo que está pasando por el middleware contiene la palabra id o _id ya sea enviado por get o por post, y si encuentra ese valor lo desencripta antes de llegar al controlador.
class EncryptMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { /** * Se recoge la request enviada por get y se recorren y desencriptan los parámetros que tiene. El controlador recoge los datos desencriptados para procesarlos correctamente */ foreach($request->route()->parameters as $key => $value){ if($key == 'id' || Str::endsWith($key, ['_id'])) { $request->route()->setParameter($key, Encryptor::decrypt($value)); } } /** * Se comprueba si el método es post. Si lo es se recorren sus parámetros y se comprueba que en los datos enviados por el formulario hay algún parámetro que se llame exactamente ID o que contenga _ID. Si la comprobación es exitosa se desencripta para que el controlador pueda procesar los datos correctamente */ if($request->isMethod('post')){ $array = []; foreach ($request->request as $key => $value) { if($key == 'id' || Str::endsWith($key, ['_id'])){ // Si lo que recibe es un array, se desencripta cada valor del array, se guarda en un array temporal y se aplica sobre el array original de la request if (is_array($value)){ foreach ($value as $key_array=>$array_item) { if($array_item != NULL){ array_push($array, Encryptor::decrypt($array_item)); } } $request->request->set($key, $array); } else { if($value != NULL){ $request->request->set($key, Encryptor::decrypt($value)); } } } } } return $next($request); } }
Web
Todas las rutas que englobe el EncryptMiddleware se verán afectadas por la desencriptación de ids
Route::middleware(‘encrypt)->group(function () { Route::get(‘/users’, ‘UserController@index); Route::get(‘/users/{id}’, ‘UserController@edit’); Route::post(‘/users/{id}’, ‘UserController@store); }
Y con todo esto ya tendremos la encriptación de ids implementada en nuestra web.