🕸️ 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.