🕸️ Cross-Site Scripting (XSS) — prevencion completa

Actualizado: 2024-12-01

XSS (Cross-Site Scripting) permite a un atacante inyectar JavaScript malicioso en la pagina. En PrestaShop, todo contenido proveniente del usuario debe escaparse antes de mostrarse en el HTML.

#XSS en PHP — htmlspecialchars

Escape en PHP antes de mostrar datos
php
<?php

// ── Escape basico para HTML ──
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// Convierte: < > " ' & en sus entidades HTML

// ── Escape para atributos HTML ──
echo '<input value="' . htmlspecialchars($value, ENT_QUOTES, 'UTF-8') . '">';

// ── Escape para URLs ──
echo '<a href="' . urlencode($url) . '">' . htmlspecialchars($text, ENT_QUOTES, 'UTF-8') . '</a>';

// ── Escape para JSON en atributos data- ──
echo '<div data-config="' . htmlspecialchars(json_encode($config), ENT_QUOTES, 'UTF-8') . '">';

// ── Clase Tools de PrestaShop ──
echo Tools::safeOutput($userInput);            // htmlspecialchars simplificado
echo Tools::htmlentitiesDecodeUTF8($string);   // Para mostrar entidades previamente codificadas

// ── NO HACER: mostrar datos sin escape ──
echo $userInput;               // ❌ VULNERABLE
echo $_POST['comment'];        // ❌ VULNERABLE
echo Tools::getValue('name');  // ❌ VULNERABLE (sin escape)

#XSS en Smarty — escape

Modificadores de escape en Smarty
smarty
{* ── Escape HTML (para contenido de texto) ── *}
{$user_input|escape:'html':'UTF-8'}

{* ── Escape para atributos HTML ── *}
<input value="{$value|escape:'html':'UTF-8'}">
<div id="{$id|escape:'html'}">...</div>

{* ── Escape para URLs ── *}
<a href="{$url|escape:'url'}">Enlace</a>

{* ── Escape para JavaScript ── *}
<script>
var name = "{$name|escape:'javascript'}";
</script>

{* ── Sin escape (solo para contenido HTML de confianza del admin) ── *}
{$product.description nofilter}
{* ⚠️ SOLO usar nofilter con contenido que ya fue saneado en el servidor *}

{* ── Smarty 3 escapa por defecto con {$var} en algunos contextos ── *}
{* ── Siempre usar |escape:'html' explicitamente para seguridad ── *}

{* ── En variables de URL ── *}
<a href="{$urls.base_url}{$page.canonical|escape:'url'}">
  {$page.title|escape:'html'}
</a>

#XSS en Twig — auto-escape

Auto-escape y raw en Twig
twig
{# Twig escapa automaticamente por defecto #}
{{ user_input }}           {# Escapado automaticamente — SEGURO #}
{{ user_input|e }}         {# Escape explicito — equivalente #}
{{ user_input|e('html') }} {# Escape HTML explicito #}

{# Para contenido HTML de confianza (admin WYSIWYG) #}
{{ product_description|raw }}
{# ⚠️ SOLO usar |raw con contenido sanitizado en el servidor #}

{# Escape para JavaScript #}
<script>
var config = {{ json_encode(config)|raw }};
</script>
{# json_encode ya escapa para JavaScript #}

{# Escape para atributos #}
<div data-url="{{ url|e('html_attr') }}">...</div>

{# Desactivar auto-escape en un bloque (usar con cuidado) #}
{% autoescape false %}
  {{ trusted_html_content }}
{% endautoescape %}

#HTML permitido — purifyHTML

Sanear HTML de usuario con Tools::purifyHTML()
php
<?php

// ── Tools::purifyHTML() usa HTMLPurifier internamente ──
// Permite HTML basico pero elimina scripts, event handlers, etc.

$comment = Tools::getValue('comment');
$safeHtml = Tools::purifyHTML($comment);
// <script>alert('xss')</script> → eliminado
// <p onclick="evil()">texto</p> → <p>texto</p>
// <b>Negrita</b> → permitido

// ── Validacion con Validate::isCleanHtml() ──
$htmlContent = Tools::getValue('description');
if (!Validate::isCleanHtml($htmlContent)) {
    $this->errors[] = 'Contenido HTML invalido';
} else {
    // El HTML es relativamente seguro (no perfecto)
    // Guardar en BD:
    $db->insert('mymodule_data', [
        'content' => pSQL($htmlContent, true) // true = allow HTML chars in pSQL
    ]);
}

// ── strip_tags() — solo texto plano, sin HTML ──
$plainText = strip_tags(Tools::getValue('text'));

// ── strip_tags() con tags permitidas ──
$allowedTags = '<p><br><strong><em><ul><ol><li>';
$filtered = strip_tags(Tools::getValue('content'), $allowedTags);

#Content Security Policy

Agregar CSP headers desde un modulo
php
<?php

// ── Agregar Content Security Policy en el FO ──
public function hookActionFrontControllerSetMedia(): void
{
    // CSP restrictiva: bloquea scripts inline y solo permite del propio origen
    header(
        "Content-Security-Policy: " .
        "default-src 'self'; " .
        "script-src 'self' 'nonce-" . base64_encode(random_bytes(16)) . "'; " .
        "style-src 'self' 'unsafe-inline'; " .
        "img-src 'self' data: https:; " .
        "connect-src 'self' https://api.miservicio.com;"
    );
}

// ── Header X-XSS-Protection (legacy, mayormente obsoleto en PS moderno) ──
header('X-XSS-Protection: 1; mode=block');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: SAMEORIGIN');
Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.