
Saneamiento de entradas de usuario en el desarrollo de plugins a medida
Desarrollar plugins a medida para WordPress requiere no solo funcionalidad, sino también una disciplina estricta en el saneamiento y validación de las entradas de usuario. Un input mal tratado puede derivar en XSS, SQL injection, pérdida de datos o problemas de rendimiento. En este artículo repasaremos principios, funciones nativas, patrones recomendados y ejemplos prácticos para que tu plugin sea seguro, consistente y fácil de auditar.
Por qué el saneamiento es imprescindible
La entrada de usuario es una superficie de ataque. Validar y sanitizar reduce riesgo, mejora la estabilidad y facilita el cumplimiento de políticas de privacidad y normas de seguridad. Dos conceptos importantes:
- Validación: comprobar que los datos cumplen las reglas (tipo, rango, formato).
- Sanitización: limpiar y normalizar los datos para evitar código malicioso o datos no deseados.
Además, hay que diferenciar entre sanitizar en entrada y escapar en salida. Sanitiza cuando guardas o procesas; escapa siempre al mostrar datos en HTML, atributos, JS o consultas SQL.
Principios prácticos para plugins a medida
- Principio de menor privilegio: limita quién puede enviar datos sensibles (capabilities).
- Valida primero, sanitiza después y escapa al final (output-escaping).
- Usa APIs nativas de WordPress cuando existan.
- Evita concatenar consultas SQL: usa consultas preparadas.
- Registra y documenta qué campo acepta qué formato para facilitar auditoría.
Funciones y técnicas nativas recomendadas
WordPress y PHP ofrecen funciones específicas. A continuación se listan las más útiles con ejemplos prácticos.
Sanitización básica y más comunes
sanitize_text_field(): ideal para campos de texto simples.sanitize_email(): valida y limpia correos.esc_url_raw(): sanitiza URLs antes de guardarlas. Para salida, usaresc_url().intval()/floatval(): convertir a números garantizando tipo.wp_kses_post()owp_kses(): permiten HTML seguro según reglas.
Escapado en la salida
Siempre escapa justo antes de imprimir datos en la vista:
esc_html()para contenido HTML.esc_attr()para atributos HTML.esc_url()para enlaces y recursos.
Protección en formularios: nonces y capacidades
Usa wp_nonce_field() y valida con wp_verify_nonce(). Además, comprueba capacidades con current_user_can() para evitar que usuarios sin permisos ejecuten acciones sensibles.
Consultas seguras con $wpdb
Nunca concatenes variables en SQL. Usa $wpdb->prepare() o las funciones de abstracción. Ejemplo:
$sql = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}mi_tabla WHERE id = %d", intval( $id ));
$rows = $wpdb->get_results( $sql );
Ejemplos prácticos: formularios, settings y REST API
1) Guardar un campo de opción desde settings
Al registrar opciones, puedes definir una función de sanitización:
register_setting('mi_plugin_group', 'mi_plugin_opcion', array(
'type' => 'string',
'sanitize_callback' => 'mi_plugin_sanitize_opcion',
));
function mi_plugin_sanitize_opcion($input) {
// Validación básica y sanitizado
$output = array();
$output['titulo'] = sanitize_text_field( $input['titulo'] );
$output['url'] = esc_url_raw( $input['url'] );
$output['activo'] = isset($input['activo']) ? 1 : 0;
return $output;
}
Esta función mantiene el contrato de datos y evita almacenar HTML o scripts no deseados.
2) Procesar un formulario con nonce
if ( isset($_POST['mi_form']) ) {
if ( ! isset($_POST['mi_nonce']) || ! wp_verify_nonce($_POST['mi_nonce'], 'accion_mi_form') ) {
wp_die('Nonce inválido');
}
if ( ! current_user_can('manage_options') ) {
wp_die('Sin permisos');
}
$nombre = sanitize_text_field( $_POST['nombre'] );
$email = sanitize_email( $_POST['email'] );
// Guardar o procesar
}
3) Endpoints REST seguros
Al registrar rutas, define esquemas de validación y sanitización en args:
register_rest_route('mi-plugin/v1', '/item', array(
'methods' => 'POST',
'callback' => 'mi_plugin_crear_item',
'permission_callback' => function() {
return current_user_can('edit_posts');
},
'args' => array(
'title' => array(
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => function($param, $request, $key) {
return is_string($param) && strlen($param) > 3;
}
),
'url' => array(
'sanitize_callback' => 'esc_url_raw'
)
)
));
Patrones avanzados y errores comunes a evitar
- No confundir validación con escapado: validar formato, escapar para presentación.
- No confiar en el cliente: cualquier validación JS debe replicarse en servidor.
- No usar
wp_ksessin reglas explícitas: define exactamente qué etiquetas y atributos permites. - No eliminar la auditable trazabilidad: guarda logs de acciones críticas con usuario y timestamp.
Uso de whitelists y blacklists
Prefiere listas blancas (whitelist) sobre listas negras. Por ejemplo, en selects o parámetros esperados, compara el input con valores permitidos antes de aceptar.
Pruebas y revisión
Incluye pruebas unitarias y pruebas de fuzzing para entradas inusuales. Revisa los puntos de entrada populares: settings, AJAX, REST API, upload handlers y shortcodes.
Recursos y referencias técnicas
Para profundizar en buenas prácticas y funciones estándar revisa la guía de seguridad de OWASP, la documentación de PHP sobre funciones de filtrado y MDN Web Docs para el manejo seguro de HTML y URL en el navegador. Estas fuentes ayudan a entender vectores de ataque y contramedidas en un contexto más amplio.
Checklist rápida antes de publicar un plugin
- ¿Todos los formularios usan nonces y comprueban capacidades?
- ¿Se sanitizan todos los inputs antes de almacenamiento?
- ¿Se escapa la salida en plantillas y atributos?
- ¿Se usan consultas preparadas para SQL?
- ¿Hay pruebas automatizadas para inputs extremos?
Conclusión
El saneamiento de entradas en plugins a medida es una disciplina técnica que combina validación, sanitización y escapado. Aplicar funciones nativas de WordPress y PHP, proteger endpoints con nonces y capacidades, y usar consultas preparadas reduce significativamente los riesgos. Además, documentar el formato esperado de cada campo y construir pruebas automatizadas facilita mantenimiento y auditoría. Implementa estas prácticas desde el diseño del plugin: seguridad y calidad comienzan en la definición de datos, no como un parche posterior.