blog twig

Filtros de Escape en Twig

Tabla de Estrategias de Escape

Filtro Alias Uso Ejemplo de entrada Ejemplo de salida Cuándo usarlo
|e |e('html') | |escape Escape HTML <script>alert('XSS')</script> &lt;script&gt;alert('XSS')&lt;/script&gt; Contenido HTML general: títulos, párrafos, texto visible
|e('html_attr') - Escape atributos HTML " onclick="alert('XSS') &quot; onclick=&quot;alert('XSS') Dentro de atributos HTML: <div title="{{ var|e('html_attr') }}">
|e('js') - Escape JavaScript '; alert('XSS'); var x=' \'; alert(\'XSS\'); var x=\' Dentro de código JavaScript: var name = '{{ var|e('js') }}';
|e('css') - Escape CSS </style><script>alert()</script> \3C \2F style\3E \3C script\3E ... Dentro de código CSS: color: {{ var|e('css') }};
|e('url') |url_encode Escape URL hello world & más hello%20world%20%26%20m%C3%A1s Parámetros de URL: ?search={{ query|e('url') }}

Ejemplos Prácticos

1. HTML - Contenido visible

{# ✅ CORRECTO #}
<h1>{{ page.title|e }}</h1>
<p>{{ page.content|raw }}</p>
{# ❌ INCORRECTO (vulnerable a XSS) #}
<h1>{{ page.title }}</h1>

2. HTML Attributes - Dentro de atributos

{# ✅ CORRECTO #}
<img src="logo.png" alt="{{ page.title|e('html_attr') }}">
<input type="text" value="{{ user_input|e('html_attr') }}">
<div data-content="{{ description|e('html_attr') }}">
{# ❌ INCORRECTO (|e solo no es suficiente en atributos) #}
<img alt="{{ page.title|e }}">

3. JavaScript - Dentro de scripts

{# ✅ CORRECTO #}
<script>
  var title = '{{ page.title|e('js') }}';
  var data = {{ json_data|json_encode|raw }};
</script>
{# ❌ INCORRECTO #}
<script>
  var title = '{{ page.title|e }}'; // No previene inyección JS
</script>

4. CSS - Dentro de estilos

{# ✅ CORRECTO #}
<style>
  .custom {
    background-color: {{ user_color|e('css') }};
  }
</style>
{# ⚠️ MEJOR: Validar colores antes #}
{% if user_color matches '/^#[0-9A-F]{6}$/i' %}
  <style>.custom { background-color: {{ user_color }}; }</style>
{% endif %}

5. URL - Parámetros de consulta

{# ✅ CORRECTO #}
<a href="/search?q={{ search_term|e('url') }}&category={{ category|e('url') }}">
  Buscar
</a>
{# ✅ También correcto #}
<a href="/search?q={{ search_term|url_encode }}">Buscar</a>
{# ❌ INCORRECTO #}
<a href="/search?q={{ search_term }}"> {# Rompe con espacios y caracteres especiales #}

Casos Especiales

Escape doble (evitar)

{# ❌ MAL - Escape doble #}
{% set title = page.title|e %}
<h1>{{ title|e }}</h1>  
{# Resultado: &amp;lt;script&amp;gt; (doblemente escapado) #}
{# ✅ BIEN - Escapar una sola vez #}
{% set title = page.title %}
<h1>{{ title|e }}</h1>

Contenido HTML confiable (raw)

{# Si el contenido YA está procesado y es seguro #}
<div>{{ page.content|markdown|raw }}</div>
{# ⚠️ NUNCA uses |raw con input del usuario sin sanitizar #}

JSON en JavaScript

{# ✅ CORRECTO - json_encode ya escapa correctamente #}
<script>
  var config = {{ settings|json_encode|raw }};
</script>
{# ❌ INCORRECTO #}
<script>
  var config = {{ settings|e('js') }}; // No convierte a JSON válido
</script>

Resumen de Mejores Prácticas

  1. Por defecto usa |e para todo contenido HTML visible
  2. Usa |e('html_attr') dentro de atributos HTML
  3. Usa |e('js') dentro de strings JavaScript
  4. Usa |e('url') en parámetros de URL
  5. Evita |raw a menos que el contenido sea 100% confiable
  6. Escapa una sola vez - al final, en el output
  7. Para JSON usa |json_encode|raw, no |e('js')