🛡️ Seguridad en queries — pSQL y casting

Actualizado: 2024-12-01

La regla de oro de seguridad en base de datos es: nunca concatenar datos de usuario en una query sin sanear. PrestaShop no usa prepared statements nativos pero proporciona funciones de escape que deben usarse SIEMPRE.

#Funciones de escape

FuncionUsoEjemplo
pSQL($str)Strings en condiciones WHERE, INSERT, UPDATEWHERE name = '" . pSQL($name) . "'
(int) $valIDs y enterosWHERE id_product = " . (int)$id
(float) $valPrecios y decimalesSET price = " . (float)$price
(bool) $valBooleanosSET active = " . (int)(bool)$active
bqSQL($table)Nombres de tablas y columnasFROM " . bqSQL($table) . "
intval($arr)Arrays de IDs con array_mapIN (" . implode(',', array_map('intval', $ids)) . ")

#Reglas por tipo de dato

Reglas de escape por tipo de dato
php
<?php

// ── IDs (integers) — SIEMPRE casting a int ──
$idProduct  = (int) Tools::getValue('id_product');
$idOrder    = (int) $_GET['id_order'];
$idCustomer = (int) Context::getContext()->customer->id;

// Uso:
$sql = 'WHERE id_product = ' . $idProduct;  // OK

// ── Strings — SIEMPRE pSQL() ──
$name     = pSQL(Tools::getValue('name'));
$search   = pSQL(Tools::getValue('q'));
$email    = pSQL(Tools::getValue('email'));

// Uso:
$sql = "WHERE name = '" . $name . "'";              // OK
$sql = "WHERE email LIKE '%" . $search . "%'";       // OK

// ── Floats/precios — SIEMPRE casting a float ──
$price   = (float) Tools::getValue('price');
$amount  = (float) $_POST['amount'];

// Uso:
$sql = 'SET price = ' . number_format($price, 6, '.', ''); // OK

// ── Arrays de IDs ──
$idList = Tools::getValue('ids');  // puede ser '1,2,3' o [1,2,3]

if (is_array($idList)) {
    $safeIds = implode(',', array_map('intval', $idList));
} elseif (is_string($idList)) {
    $safeIds = implode(',', array_map('intval', explode(',', $idList)));
}

// Uso:
$sql = 'WHERE id_product IN (' . $safeIds . ')';  // OK

// ── Nombres de tablas y columnas ──
$table  = bqSQL(Tools::getValue('table'));
$column = bqSQL(Tools::getValue('sort_by'));

// Uso:
$sql = 'SELECT * FROM `' . _DB_PREFIX_ . $table . '` ORDER BY `' . $column . '`';

#Ejemplos correctos vs incorrectos

Comparacion de queries seguras vs vulnerables
php
<?php

// ═══════════════════════════════════════════════
// ❌ VULNERABLE — SQL Injection posible
// ═══════════════════════════════════════════════

$id   = $_GET['id'];         // Sin validar
$name = $_POST['name'];      // Sin escapar

$sql = 'SELECT * FROM ps_product WHERE id_product = ' . $id;
// Payload: id=1 OR 1=1 → SELECT * FROM ps_product WHERE id_product = 1 OR 1=1

$sql = "SELECT * FROM ps_product WHERE name = '" . $name . "'";
// Payload: name='; DROP TABLE ps_product; --

// ═══════════════════════════════════════════════
// ✅ CORRECTO — Queries seguras
// ═══════════════════════════════════════════════

$id   = (int) Tools::getValue('id');      // Casting a int
$name = pSQL(Tools::getValue('name'));    // Escape de string

$sql = 'SELECT * FROM `' . _DB_PREFIX_ . 'product` WHERE id_product = ' . $id;
// id=1 OR 1=1 → id_product = 1 (0 si no es numero)

$sql = 'SELECT * FROM `' . _DB_PREFIX_ . 'product` WHERE name = \'' . $name . "'";
// name='; DROP TABLE... → name = \'; DROP TABLE...\' (escapado)

// ── DbQuery es mas limpio para queries complejas ──
$products = Db::getInstance()->executeS(
    (new DbQuery())
        ->select('*')
        ->from('product')
        ->where('id_product = ' . (int) $id)
        ->where("name = '" . pSQL($name) . "'")
);

#Validacion antes de consultar

Validar datos antes de usar en queries
php
<?php

// ── Validar con la clase Validate de PrestaShop ──
$idProduct = (int) Tools::getValue('id_product');

// Validar que el ID existe antes de usarlo
if (!Validate::isLoadedObject(new Product($idProduct))) {
    // El producto no existe o ID invalido
    Tools::redirect('index.php?controller=404');
}

// ── Validar email antes de query ──
$email = Tools::getValue('email');
if (!Validate::isEmail($email)) {
    throw new \InvalidArgumentException('Email invalido');
}
$safeEmail = pSQL($email);

// ── Lista blanca para ORDER BY (evitar inyeccion en columnas) ──
$allowedSortColumns = ['name', 'price', 'date_add', 'sort_order'];
$sortBy = Tools::getValue('sort_by', 'date_add');

if (!in_array($sortBy, $allowedSortColumns, true)) {
    $sortBy = 'date_add'; // Valor por defecto si no es valido
}

$sortOrder = Tools::getValue('sort_order', 'DESC');
$sortOrder = strtoupper($sortOrder) === 'ASC' ? 'ASC' : 'DESC'; // Solo ASC o DESC

$sql = 'SELECT * FROM ... ORDER BY `' . $sortBy . '` ' . $sortOrder;
Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.