🔐 Sistema de tokens CSRF — prevencion de ataques

Actualizado: 2024-12-01

PrestaShop usa tokens para prevenir ataques CSRF (Cross-Site Request Forgery). Cualquier formulario o peticion AJAX que realice una accion con efectos secundarios debe incluir y verificar un token.

#Tipos de token en PrestaShop

TipoFuncionDuracionUso
Token estatico del FOTools::getToken(false)Sesion del clienteFormularios y AJAX en FO
Token de pagina del FOTools::getToken(true)Unico por paginaFormularios de una sola accion
Token del BOTools::getAdminToken(...)Sesion del empleadoAcciones en el Back Office
Token de confirmacionTools::encrypt($data)Unico por datosLinks de email, confirmaciones

#Token estatico del Front Office

Generar y verificar token en FO
php
<?php

// ── En el FrontController — generar y asignar ──
public function initContent(): void
{
    parent::initContent();

    // Token estatico — igual para toda la sesion del cliente
    $staticToken = Tools::getToken(false);

    $this->context->smarty->assign([
        'static_token' => $staticToken,
        'my_form_data' => $this->getFormData(),
    ]);
}

// ── En el metodo de procesado del formulario ──
public function postProcess(): void
{
    if (!Tools::isSubmit('submitMyForm')) {
        return;
    }

    // Verificar el token
    $token = Tools::getValue('token');
    if (!$token || $token !== Tools::getToken(false)) {
        $this->errors[] = $this->trans(
            'Invalid token.',
            [],
            'Shop.Notifications.Error'
        );
        return;
    }

    // Procesar el formulario...
}
Incluir token en templates Smarty (FO)
smarty
{* En el template del formulario *}
<form method="post" action="{$urls.pages.my_page}">
  {* Campo oculto con el token *}
  <input type="hidden" name="token" value="{$static_token}">
  <input type="hidden" name="submitMyForm" value="1">

  <div class="form-group">
    <label for="name">Nombre</label>
    <input type="text" id="name" name="name" class="form-control">
  </div>

  <button type="submit" class="btn btn-primary">Enviar</button>
</form>

#Token del Back Office

Tokens en el Back Office
php
<?php

// ── Token del AdminController actual ──
$token = Tools::getAdminToken(
    'AdminMyController'                                   // Nombre del controller
    . (int) Tab::getIdFromClassName('AdminMyController')  // ID del tab
    . (int) Context::getContext()->employee->id           // ID del empleado
);

// ── Token simplificado (PS 1.7+) ──
$token = Tools::getAdminTokenLite('AdminMyController');

// ── Verificar el token en el controller ──
if (!Tools::isTokenValid()) {
    // Token invalido o expirado
    die('Token invalido');
}

// ── Usar en URLs del BO ──
$url = $this->context->link->getAdminLink('AdminMyController') . '&action=myAction';
// getAdminLink ya incluye el token automaticamente

// ── En el template del BO (Smarty) ──
// {$token} esta disponible automaticamente en todos los templates del BO
// <input type="hidden" name="token" value="{$token}">

#Tokens en peticiones AJAX

Enviar y verificar tokens en peticiones AJAX
javascript
// ── FO: Token en fetch() ──
// El token debe estar disponible como variable JS
// En el FrontController:
// Media::addJsDef(['mymodule' => ['token' => Tools::getToken(false)]]);

const response = await fetch('/module/mymodule/ajax', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    action: 'myAction',
    token: mymodule.token,  // Token desde variable JS
    data: JSON.stringify({ key: 'value' }),
  }),
});

// ── FO: jQuery AJAX con token ──
$.ajax({
  url: mymodule.ajax_url,
  type: 'POST',
  data: {
    action: 'myAction',
    token: mymodule.token,
    id: productId,
  },
  success: function(response) { /* ... */ },
});

// ── BO: Token en AJAX (el BO lo tiene en window.token) ──
fetch(window.location.pathname, {
  method: 'POST',
  body: new URLSearchParams({
    ajax: 1,
    action: 'myAction',
    token: window.token,  // Token del BO
  }),
});

#Verificar tokens en formularios

Verificacion robusta de token en ModuleFrontController
php
<?php

public function postProcess(): void
{
    // ── Patron completo de verificacion ──

    // 1. Verificar que es un submit del formulario
    if (!Tools::isSubmit('submitMyAction')) {
        return;
    }

    // 2. Verificar token CSRF
    if (!Tools::isTokenValid('mymodule')) {
        // PS 1.7+ tiene isTokenValid para tokens especificos
        $this->errors[] = 'Token invalido';
        return;
    }
    // Alternativa para PS 1.6+:
    // $token = Tools::getValue('token');
    // if ($token !== Tools::getToken(false)) { ... }

    // 3. Verificar autenticacion si es necesario
    if (!$this->context->customer->isLogged()) {
        Tools::redirect($this->context->link->getPageLink('my-account'));
        return;
    }

    // 4. Procesar la accion (ya verificada)
    $this->processMyAction();
}
Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.