FlawOpen

flawopen.com/Vulnerabilities/Server-Side Template Injection (SSTI)

Server-Side Template Injection (SSTI): causa y corrección

Impact depends on reachable template behavior CWE-1336 Template Engine Security
💡 Explicación sencilla — datos o instrucciones de impresión

Imagina una empresa que imprime cartas con la plantilla “Hola, {{ name }}”. El proceso seguro entrega a la impresora una plantilla fija y el nombre del cliente como dato. El proceso inseguro inserta el texto del cliente en las instrucciones de la plantilla antes de procesarla. Si el motor interpreta ese texto como sintaxis de plantilla, existe Server-Side Template Injection. El impacto depende del motor, su configuración, los objetos disponibles y los privilegios del proceso; evaluar una expresión no implica ejecutar comandos del sistema.

Objetivo: construcción del código fuente de plantillas y renderizado en el servidor en motores como Jinja, Twig, FreeMarker y ERB.
Límite vulnerable: se compila o analiza entrada no confiable como código de plantilla, en vez de pasarla como valor a una plantilla confiable.
Impacto: evaluación de expresiones; consecuencias posteriores dependen de las capacidades del motor, objetos expuestos, sandbox y privilegios.
Corrección: mantén el código fuente confiable y estático; pasa la entrada no confiable solo como dato de contexto. Aísla las plantillas creadas por usuarios si esa función es necesaria.

Conceptos clave y términos del subsistema

Motor de plantillas
Componente del servidor, como Jinja o Twig, que analiza sintaxis de plantillas y genera salida a partir de una plantilla y datos de contexto.
Inyección en el código fuente de la plantilla
Fallo por el que texto externo se incluye en el código fuente antes del análisis, de modo que el motor lo interpreta como sintaxis y no como dato.
Datos de contexto
Valores pasados por separado a una plantilla fija y confiable. La separación correcta evita que un valor como {{ 7 * 7 }} se vuelva a analizar como código fuente.
CWE-1336
Neutralización incorrecta de elementos especiales usados en un motor de plantillas. Describe el procesamiento inseguro de entrada externa como sintaxis de plantilla.

Flujo de ataque paso a paso

Step 1

La entrada controlada por el atacante llega a un punto de plantilla

Un campo, valor de perfil almacenado u otra cadena influida externamente llega al código que crea o compila una plantilla. El fallo está en construir el código fuente, no en renderizar un valor.

Step 2

La aplicación mezcla datos e instrucciones de plantilla

La aplicación interpola la cadena no confiable en el código fuente (por ejemplo, una f-string enviada a render_template_string) en lugar de usar una plantilla estática y un valor de contexto separado.

Step 3

El motor analiza el código combinado

El motor interpreta las expresiones según su lenguaje y configuración. Una expresión aritmética inocua puede demostrar la evaluación; eso por sí solo no demuestra acceso a archivos, secretos o comandos del sistema.

Step 4

El impacto depende de las capacidades y el despliegue

El impacto adicional depende de los objetos y funciones accesibles, el sandbox, las restricciones del motor y los privilegios del proceso web. RCE es posible en algunas configuraciones, pero no es consecuencia automática de todo SSTI.

Mecanismo y causa raíz

SSTI ocurre cuando la aplicación permite que texto influido externamente forme parte del programa de plantilla antes de analizarlo. En Flask/Jinja, el patrón vulnerable crea una nueva cadena de código fuente con datos de la petición y la pasa a render_template_string(); el escape HTML protege la salida y no convierte código fuente no confiable en datos. La corrección carga una plantilla fija y pasa el valor por separado como contexto. Si los usuarios deben crear plantillas, expón un modelo de datos mínimo y renderízalas en un worker aislado con límites de recursos. El sandbox aporta defensa en profundidad, pero no justifica exponer secretos u objetos potentes.

flask_app.py (Patrón vulnerable)Vulnerable
# VULNERABLE: request data becomes template source before parsing.
from flask import Flask, request, render_template_string
app = Flask(__name__)

@app.route("/greet")
def greet():
    name = request.args.get("name", "Guest")
    # A value such as {{ 7 * 7 }} is interpreted by the template engine.
    source = f"<h1>Welcome, {name}!</h1>"
    return render_template_string(source)
flask_app.py (Protegido)Protegido
# HARDENED: fixed template source; request data stays a context value.
from flask import Flask, request, render_template
app = Flask(__name__)

@app.route("/greet")
def greet():
    name = request.args.get("name", "Guest")
    return render_template("greet.html", name=name)

# templates/greet.html is trusted, static source:
# <h1>Welcome, {{ name }}!</h1>

Secuencia de ataque y ejemplo conceptual

Reglas de prevención y protección de ingeniería

Fuentes primarias y temas de seguridad relacionados:Directorio completo de seguridad →
CWE-1336: inyección en motores de plantillas → OWASP WSTG: Server-Side Template Injection → Jinja: sandbox y consideraciones de seguridad → Jinja API: plantillas y loaders → Flask: renderizado de plantillas →