🛡️ 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
| Funcion | Uso | Ejemplo |
|---|---|---|
| pSQL($str) | Strings en condiciones WHERE, INSERT, UPDATE | WHERE name = '" . pSQL($name) . "' |
| (int) $val | IDs y enteros | WHERE id_product = " . (int)$id |
| (float) $val | Precios y decimales | SET price = " . (float)$price |
| (bool) $val | Booleanos | SET active = " . (int)(bool)$active |
| bqSQL($table) | Nombres de tablas y columnas | FROM " . bqSQL($table) . " |
| intval($arr) | Arrays de IDs con array_map | IN (" . 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.