Blade: definir variables y mostrar HTML sin riesgo de XSS

Blade: definir variables y mostrar HTML sin riesgo de XSS
Respuesta rápida

En una vista Blade, declara una variable con @php $nom = '…'; @endphp, o pásala desde el controlador con view('vue', compact('nom')). Para mostrarla, {{ $nom }} escapa el HTML y {!! $nom !!} lo inserta tal cual. Usa la segunda forma solo con contenido de confianza: ahí es donde se crean los fallos XSS.

Dos necesidades vuelven una y otra vez en una vista Blade: declarar ahí una variable y decidir si su contenido debe escaparse o mostrarse tal cual. Lo segundo no es un detalle de comodidad: es el punto exacto por donde se cuela un fallo XSS.

Publicado en el itinerario Desarrollo web. Esta guía cubre las dos cosas: hacer llegar una variable a la vista, declarar una sobre la marcha y elegir entre {{ }} y {!! !!} con conocimiento de causa. Ejemplos ejecutados en Laravel 13.30.1 con PHP 8.4.25.

Pasar una variable desde el controlador

Es la vía normal, y la que conviene priorizar: la vista muestra, el controlador decide. El segundo argumento de view() es un array asociativo en el que cada clave se convierte en una variable.

app/Http/Controllers/DashboardController.php
return view('dashboard', ['name' => 'Gekko']);
resources/views/dashboard.blade.php
<p>Bienvenue {{ $name }} !</p>

Para mostrar una fecha procedente de un modelo, consulta la gestión de fechas con Carbon.

Cuando los valores ya están en variables PHP, compact() evita repetir los nombres:

php
$name    = 'Gekko';
$reseau  = 'gekkode';

return view('dashboard', compact('name', 'reseau'));

with() permite añadir una de paso, algo práctico cuando el valor se calcula en el último momento:

php
return view('dashboard', compact('name'))->with('extra', 'valeur ajoutée');

Si una variable puede faltar, dale un valor de reserva en la vista en lugar de añadir una condición:

php
{{ $inconnu ?? 'valeur par défaut' }}

Declarar una variable directamente en la vista

Cuando el valor solo sirve para mostrarlo, la directiva @php permite declararlo ahí mismo:

resources/views/dashboard.blade.php
@php
    $nom = 'Gekko';
    $classe = $task->active ? 'is-active' : 'is-done';
@endphp

<p class="{{ $classe }}">Bonjour {{ $nom }}</p>

Las etiquetas PHP clásicas también funcionan, pero @php resulta más legible y coherente con el resto de la plantilla.

Dos directivas cubren casos vecinos sin pasar por una variable. @class construye un atributo de clase a partir de condiciones:

php
<div @class(['actif' => $task->active, 'masque' => $task->hidden])></div>

<!-- rendu quand active vaut true et hidden false -->
<div class="actif"></div>

Y @once garantiza que un bloque se renderice una sola vez, aunque la vista se incluya varias veces en la página:

php
@once
    <script src="/js/carte.js" defer></script>
@endonce
Hasta dónde llega lo razonable

@php vale para un valor de presentación. En cuanto entra en juego una consulta a la base de datos, un cálculo de negocio o un bucle de transformación, ese código pertenece al controlador, a un componente Blade o a un accesor del modelo. Una vista que consulta la base genera consultas N+1 invisibles desde el controlador.

Mostrar HTML: {{ }} frente a {!! !!}

Blade compila {{ $variable }} en una llamada a la función e(), que escapa los caracteres especiales. Se ve en la vista compilada, dentro de storage/framework/views:

php
<?php echo e($nom); ?>

Consecuencia: si la variable contiene etiquetas, se muestran con todas sus letras en lugar de interpretarse.

php
@php $html = '<strong>gras</strong>'; @endphp

{{ $html }}     {{-- affiche : &lt;strong&gt;gras&lt;/strong&gt; --}}
{!! $html !!}   {{-- affiche : gras, en gras --}}

La sintaxis {!! !!} inserta el valor sin ninguna transformación. Es lo que hace falta para mostrar el cuerpo de un artículo redactado en HTML, y es también la puerta de entrada de una inyección de script si el valor viene de un usuario.

La regla que no hay que saltarse

Usa {!! !!} únicamente con HTML cuyo origen controlas, o que se ha limpiado antes. Con el valor <script>alert(1)</script>, {{ }} muestra el texto mientras que {!! !!} ejecuta el script en el navegador del visitante. Los dos comportamientos se han verificado.

Para el contenido que llega de un formulario, que habrás tenido cuidado de proteger contra los envíos automatizados, limpia antes de mostrar. Una biblioteca como HTML Purifier aplica una lista blanca de etiquetas, en su defecto, guarda Markdown y conviértelo al renderizar, lo que te deja el control de las etiquetas generadas.

Los casos particulares útiles

Para pasar datos a JavaScript, @json produce un literal válido y escapado:

php
<script>
    const config = @json(['id' => $task->id, 'titre' => $task->title]);
</script>

Para mostrar llaves sin que Blade las interprete, por ejemplo en una plantilla destinada a un framework JavaScript, @verbatim neutraliza el bloque entero:

php
@verbatim
    <div id="app">{{ message }}</div>
@endverbatim

Para una sola expresión basta con precederla de una arroba: @{{ message }}.

No llames a e() dentro de llaves dobles

{{ e($valeur) }} escapa dos veces: <b> se convierte en &amp;lt;b&amp;gt; y se muestra literalmente en pantalla. {{ }} ya lo hace por ti. La función e() solo sirve fuera de Blade, o dentro de un {!! !!} sobre una porción concreta.

Errores frecuentes

{!! !!} sobre un valor de usuario La cadena <script>alert(1)</script> se ejecuta de verdad en el navegador del visitante. Limpia el HTML antes, o guarda Markdown.
{{ e($valeur) }} escapa dos veces Las llaves dobles ya llaman a e(). El resultado muestra las entidades en pantalla en lugar del texto.
Una consulta a la base dentro de @php La vista lanza consultas que el controlador no ve, lo que produce N+1 invisibles. Lleva la lógica al controlador o a un componente.
Olvidar el valor de reserva en una variable opcional {{ $var ?? 'défaut' }} evita un error si el controlador no la ha pasado en todos los casos.

BladeLaravelPHP

Damien Flandrin Desarrollador web desde 2010, creador de Gekkode y de Email Impact. Cada artículo se prueba en un proyecto real antes de publicarse. Contacto
Newsletter

Las nuevas pruebas, tutoriales y proyectos, por correo.

Pruebas reproducibles, código versionado, resultados fechados. Nunca spam.