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> |
<script>alert('XSS')</script> |
Contenido HTML general: títulos, párrafos, texto visible |
| |e('html_attr') |
- |
Escape atributos HTML |
" onclick="alert('XSS') |
" onclick="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: &lt;script&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
- Por defecto usa
|e para todo contenido HTML visible
- Usa
|e('html_attr') dentro de atributos HTML
- Usa
|e('js') dentro de strings JavaScript
- Usa
|e('url') en parámetros de URL
- Evita
|raw a menos que el contenido sea 100% confiable
- Escapa una sola vez - al final, en el output
- Para JSON usa
|json_encode|raw, no |e('js')