Al igual que en la primera parte de esta serie, este artículo es una guía elemental de buenas prácticas en seguridad al desarrollar un plugin de WordPress. Gran parte del contenido ha sido extraído de la documentación de WordPress y traducido al castellano. Échale un al anterior y siguiente artículos de esta serie:
Elementos esenciales para crear un plugin en WordPress
Cómo traducir nuestro plugin de WordPress (o tema)
Índice de Contenido
Principios básicos de seguridad
- Nunca debemos confiar en cualquier elemento introducido por el usuario
- Debemos escapara el contenido lo más tarde posible
- Debemos escapar todo lo que provenga de fuentes no fiables. Como bases de datos, usuarios, etc.
- El saneamiento está bien, pero la validación es mucho mejor
Saneamiento de datos
El saneamiento de datos es el proceso en el que se aseguran, limpian y filtran los datos introducidos por el usuario. Un ejemplo claro es cuando un usuario rellena un formulario. En este caso el usuario debería introducir los datos apropiados para cada campo. Pero, como hemos dicho anteriormente, nunca debemos confiar en los datos introducidos por un visitante de nuestra web.
Aunque la validación de datos es mucho mejor, existen casos en los que no los datos no son lo suficientemente concretos, entonces es mejor aplicar el saneamiento.
Ejemplo práctico de saneamiento
Al crear un formulario personalizado tendremos que contar con una función que gestione los datos introducidos por el usuario para, por ejemplo, subirlos a la base de datos.
En este ejemplo podríamos decir que el usuario ha introducido un título para el nombre de su grupo de música. Antes de enviarlo a la base de datos deberíamos verificar que ese contenido es alfanumérico mediante la función sanitize_text_field. Esta función realizaría lo siguiente:
- verifica que sigue codificación UTF-8 válida
- Convierte caracteres “<” en entidades HTML
- Elimina etiquetas HTML
- Elimina saltos de línea y espacios extra
- Elimina espacios al comienzo y al final
- Elimina octetos: %[a-f0-9]{2}
Funciones de WordPress para saneamiento de datos
WordPress nos proporciona un gran número de funciones concretas para cada ocasión:
- sanitize_email(): Sanea correos electrónicos Eliminando caracteres no permitidos.
- sanitize_file_name(): Sanea nombres de archivo reemplazando espacios en blanco por guiones.
- sanitize_hex_color(): Sanea colores hexadecimales devolviendo el formato correcto (#RRGGBB).
- sanitize_hex_color_no_hash(): Igual que el anterior pero devolviendo el código sin almohadilla.
- sanitize_html_class(): Esta función sanea una clase HTML verificando que solo contenga caracteres válidos.
- sanitize_key(): Sanea claves (contraseñas) eliminando caracteres no permitidos. Se permite solo caracteres alfanuméricos en minúsculas, guiones y guiones bajos.
- sanitize_meta(): Usado para sanear metadatos. Esta función no sane nada por sí sola, debemos conectar filtros personalizados para hacer que funcione. Tendremos que usarla del siguiente modo: sanitize_{$meta_type}_meta_{$meta_key}
- sanitize_mime_type(): Esta función sanea tipos MIME, por ejemplo “text/html”
- sanitize_option(): Sanea valores basados en la naturaleza de la option.
- sanitize_sql_orderby(): Nos asegura que un string sea un ORDER BY válido para SQL.
- sanitize_term(): Sanea todos los campos de una taxonomía
- sanitize_term_field(): Prepara el valor para usarlo en un texto o en otro lugar.
- sanitize_text_field(): Sanea un string introducido por el usuario o proveniente de la base de datos.
- sanitize_textarea_field(): Sanea un string multilínea introducido por el usuario o proveniente de la base de datos.
- sanitize_title(): Sanea un título para poder usarlo en una URL o en un atributo HTML.
- sanitize_title_for_query(): Sanea un título para usarlo en una consulta de la base de datos.
- sanitize_title_with_dashes(): Sanea un título reemplazando espacios en blanco por guiones.
- sanitize_user(): Sanea nombres de usuario eliminando los caracteres no seguros.
- sanitize_url(): Sanea URLs para usarla en la base de datos o en una redirección.
- wp_kses(): Sanea texto y elimina HTML no permitido.
- wp_kses_post(): Limpia el HTML no permitido en post_content.
Validación de datos
La validación es el proceso por el que se comprueba que los datos introducidos siguen un patrón concreto. Por ejemplo, si el valor que esperamos recibir es un DNI, sabremos que está formado por 8 números y una letra. Entonces, para validar que se ha introducido un DNI tendremos que comprobar que los primeros 8 caracteres son números enteros y que el noveno y último es una letra.
La validación de datos se puede realizar de diferentes modos. Algunas de las funciones PHP o de WordPress que podemos usar son las siguientes:
- count(): Nos permite contar cuántos elementos hay en un array
- in_array(): Comprueba si un elemento está en un array
- is_email(): Valida si un string es un correo electrónico válido
- strlen(): Comprueba si un string tiene el número esperado de caracteres
- preg_match(): Busca coincidencias en expresiones regulares
- str_pos(): Devuelve la posición de un substring en un string
- username_exists(): Comprueba si el nombre de usuario existe
- validate_file: Comprueba que el “path” de un archivo es real
- is_numeric(): Comprueba si es un valor numérico
- intval(): Convierte en número entero
- floatval(): convierte en decimal
Ejemplo práctico de validación de datos
Si queremos verificar un código pasado por email para, por ejemplo, hacer un descuento, y sabemos que ese código está formado por 3 letras iniciales “API” y 4 números que suman 30 podríamos hacer lo siguiente:
<?php
$str = 'API5988';
$letras = substr($str, 0, 3);
$numeros = str_replace('API', '', $str);
$array_numeros = str_split($numeros);
$total = 0;
foreach( $array_numeros as $numero ) {
$total += intval( $numero );
}
if ( 7 === strlen( $str ) && is_numeric( $numeros ) && 'API' === $letras && 30 === $total ) {
echo 'validado!';
}
Escapado de datos
Cuando escribimos código para el front end debemos asegurarnos de que los datos dinámicos contienen el valor correcto. Por ejemplo, si obtenemos de la tabla wp_options un string que funcione como algún tipo de atributo HTML deberíamos verificar que contiene el valor adecuado. Para esto WordPress nos brinda una serie de funciones:
- esc_html(): Sirve para escapar contenido entre etiquetas HTML. Esta función elimina todo rastro de código HTML
- esc_js(): Se usa para código JavaScript en línea. Como el típico onclick
- esc_url(): Sirve para escapar una URL. Por ejemplo, en el atributo src de una imagen
- esc_url_raw(): Para guardar una URL en la base de datos
- esc_xml(): Para escapar bloques XML
- esc_attr(): Para escapar atributos HTML
- esc_textarea(): Para escapar contenido en un textarea
- wp_kses(): Como ya hemos explicado más arriba, sanea texto y elimina HTML no permitido
- wp_kses_post(): También se explica más arriba.
- wp_kses_data(): Permite solo el HTML permitido en los comentarios
Escapando con localización
La localización (localization), que no sé si estoy traduciéndolo bien al 100%, sirve para reconocer ciertas partes del texto y poder traducirlas posteriormente.
Para escapar con localización en WordPress contamos con las siguientes funciones:
- esc_html__()
- esc_html_e()
- esc_html_x()
- esc_attr__()
- esc_attr_e()
- esc_attr_x()
Ejemplo práctico de escapado de datos
Si queremos obtener el título una película de la base de datos podríamos hacer lo siguiente:
<h4><?php echo esc_html( $title ); ?></h4>
Nonces
Los nonces son números de un solo uso (“number used once”) con una validez por defecto de 1 día, que ayudan a proteger URLs y formularios de un posible uso malicioso o simplemente erróneo. La teoría es muy simple, es un valor que se crea en el origen y se verifica en el destino.
Por qué usar nonces
En wordpress muchas operaciones se realizan como resultado de “una señal” en la URL. Es decir, un valor mediante $_GET. Si alguien usara este método de forma negativa podría, por ejemplo, eliminar nuestras páginas o posts. Este sería un motivo, pero también nos encontramos con los formularios. Usar un nonce en un formulario nos dará un plus de seguridad, ya que como bien sabemos, los formularios son uno de los elementos más delicados en cuanto a seguridad.
Funciones usadas para trabajar con nonces
- wp_nonce_url(): Devuelve una URL con el nonce incorporado
- wp_nonce_field(): Devuelve un campo de tipo hidden con el nonce para su uso en formularios
- wp_create_nonce(): Devuelve un nonce
- check_admin_referer(): Verifica un nonce usado en en el panel de administración de WordPress
- check_ajax_referer(): Verifica un nonce usado en una consulta AJAX
- wp_verify_nonce(): Verifica un nonce en cualquier otro contexto
- wp_nonce_ays(): Normalmente usado cuando la verificación del nonce falla. Dependiendo del argumento devuelve “El enlace que has seguido ha caducado” o un log-out “Estás intentando salir de…”
Modificar el mensaje de error
Si lo deseamos, podemos modificar el mensaje de error cuando la verificación del nonce falla mediante el filtro gettext:
function my_nonce_message ( $translation ) {
if ( 'Are you sure you want to do this?' === $translation ) {
return '¡Estás yendo por donde no es!';
}
return $translation;
}
add_filter( 'gettext', 'my_nonce_message' );
En nuestra agencia de Marketing Online también nos dedicamos a desarrollar plugins WordPress para que todo esté perfecto.