Seguridad a la hora de crear entradas en WordPress

INDICE
0.-Resumen
1.-Introducción
2.-Buscando un algoritmo
3.-Implementando nuestro sistema en WordPress
3.1.-Añadiendo posts a nuestra base de datos
3.1.1.-Analizando la tabla de wp_posts
3.1.2.-Nuestra consulta
3.1.3.-La tabla wp_postmeta
3.2.-Implementando la seguridad en nuestra aplicación
3.3.-Conclusión y funcionamiento final
4.-Códigos finales completos

0.-Resumen

En este post intentaré explicar un método casero para poder escribir en nuestro respectivo blog de WordPress desde cualquier lugar sin tener que preocuparnos en que nos estén analizando el tráfico. Ideal por si estamos en una red que no es la nuestra, como en casa de algún conocido o en un hotel.

1.-Introducción

Una de las responsabilidades que tenemos/tienen los bloggers es de mantener actualizado nuestro blog que tanto queremos, incluso si nos encontramos en otros lugares. Sin embargo, no todos disponemos de 3G y algunos tenemos que ir mendigando WiFi por los hoteles, o en casa de un pariente. Yo, con lo paranoico que soy que incluso uso la versión encriptada de Google, siempre trato de intentar comprometer lo menos posible mis datos.

Si no tenemos puesto SSL en nuestro blog de WordPress, y nos logueamos en una red que puedan tener monitorizada, fácilmente nos sacan nuestro user y pass, por lo que tendremos que buscar una alternativa para loguearnos que sea más segura que nuestra propia cuenta.

2.-Buscando un algoritmo

Una idea que se me ha ocurrido para esto, es identificarnos con un hash que varíe, de forma que si es obtenido por algún tercero, no le sirva para nada. Lo más fácil de recordar que varíe, es el tiempo, así que podriamos usar un algoritmo basado en:

  • Caracteres alfanuméricos.
  • Caracteres especiales.
  • Caracteres que varien automáticamente (en función del tiempo).
  • Caracteres que varien de forma controlada.
  • Mayúsculas y minúsculas.

Un ejemplo podria ser en base al siguiente:
///XXlipmanYY///
En donde XX es el dia del mes, y YY la hora. He elegido este par de números debido a que varian lo suficientemente rápido como para que no se reutilicen, y lo suficientemente lento como para controlarlos. Además de esto, usaremos un truco para añadir un caracter aleatorio fácil de recordar. Dependiendo del dia de la semana (lunes, martes…) teclearemos una tecla debajo de los números, dependiendo del dia que sea. Por ejemplo, cuando sea lunes, pulsamos la Q, cuando sea martes la W… (notese que seria en mayúsculas).

Ejemplo completo:
Lunes 20 a las 8AM: ///20Qlipman8///
Martes 21 a las 15PM: ///21lWipman15///
Viernes 24 a las 2AM: ///24lipmTan2///

Notese que las letras mayúsculas “aleatorias” las vamos alternando en las posiciones entre la palabra “lipman”. Para que quede bien, necesitamos palabras de 6 caracteres.
De aquí también podemos descatar lo imprescindible que es que una contraseña no empiece por números o por letras cercanas a los extremos del alfabeto, para que sea todavia más improbable que un brute force nos descubra la contraseña.

3.-Implementando nuestro sistema en WordPress

Ya tenemos el diseño (respecto a la seguridad) de nuestra aplicación, por lo que queda la parte más entretenida: realizar una aplicación para comunicarnos con WordPress implementando nuestro sistema. Voy a poner solo lo que es esencial: comunicación entre nuestra aplicación y la base de datos para añadir entradas. Por nuestra cuenta podriamos desarrollar más cosas, como una mejor interfaz gráfica, pero esto lo dejaré a parte ya que seguramente dejaré esto como un plugin (ya hablaré más adelante) y haré mejoras en todo.

3.1.-Añadiendo posts a nuestra base de datos

Para nuestra suerte, la base de datos de WordPress (y en general la de cualquier CMS) suele ser sencillita, así que no deberiamos de tener problemas para manejarla.
Lo primero de todo, aprovecharemos el fichero de wp-config.php para conectar a nuestra base de datos. Este fichero es el primero que se genera cuando creamos nuestro sitio en WordPress, y contiene los datos de nuestra base de datos: nombre, usuario, contraseña… Así que le haremos un include.

3.1.1.-Analizando la tabla de wp_posts

Lo interesante de este tipo de situaciones, es analizarlas y aprovecharlas para intentar entender el funcionamiento interior de WordPress, por ello, no me limitaré a hacer, copiar y pegar código simplemente, sino que se intentará explicará todo.

A continuación, vamos a escribir los campos que tendremos que tocar (osease, todos menos los que suelen ir a NULL), explicandolos un poco, sobre todo, los que sean menos obvios.

Nombre Comentario
ID Id (PK) del post
post_author ID del autor
post_date Fecha de escritura
post_date_gmt Fecha de escritura (GMT)
post_content Contenido del post
post_title Titulo
post_status Estado: publicado, borrador..
comment_status Permitir comentarios
ping_status Permitir pingback
post_name Nombre parseado (*)
post_modified Fecha de modificación
post_modified_gmt Fecha de modificación (GMT)
post_parent A quien pertenece (*)
guid URL parseada (*)
menu_order Orden: 0
post_type Indica si es un post
comment_count Contador de comentarios

-Nombre parseado: Se obtiene en función del titulo. Si tenemos el siguiente titulo: “Hola Mundo”, obtendremos: “hola-mundo”. Osease, lo que aparece (si lo tenemos configurado así) de enlace.

-A quien pertenece: Esto solo hace referencia a los borradores y demás. Con respecto a los posts publicados, ni caso.

-URL parseada: Esta seria la URL del post en el caso de tenerlo configurado de manera que se viera la ID en la URL. Un ejemplo, en el caso de delanover, si escribiesemos un post con ID 1234, este campo seria: http://delanover.com/?p=1234

3.1.2.-Nuestra consulta

De momento, llevamos lo siguiente:

//Incluimos los datos de configuración de nuestra base de datos
include('wp-config.php');

mysql_connect(DB_HOST, DB_USER, DB_PASSWORD) or die("Error al conectar");

//Creamos la consulta
$consulta_principal = "INSERT INTO `wp_posts`(`ID`, `post_author`, `post_date`, `post_date_gmt`,
	`post_content`, `post_title`, `post_status`, `comment_status`, `ping_status`,
	`post_name`,`post_modified`, `post_modified_gmt`,
	`post_parent`, `guid`, `menu_order`, `post_type`, `comment_count`) VALUES
	('$id', 1,$fecha,$fecha_gmt,'$contenido','$titulo','publish','open','open',
	'$titulo_parseado',$fecha,$fecha_gmt,0,'$id_parseada',0,'post',0)";
		
$resultado_principal = mysql_query($consulta_principal);

Como podemos observar en nuestra consulta, hay variables y valores constantes. Me limitaré a contar un poco las variables al igual que la forma de obtenerlas, ya que los valores constantes son un tanto redundantes.

-ID: Es la ID que tendrá nuestro post. Esta ID se consigue obteniendo el valor máximo de todas las IDs que ya hay, incrementando su valor en uno, de la siguiente manera:

SELECT MAX(ID) from `wp_posts`

-Fecha: Es la fecha de creación de nuestra entrada. Tiene un formato específico y estricto que deberemos seguir para que nos funcione correctamente. El formato es el siguiente: AAAA-MM-DD HH:MM:SS
Obtendremos este formato de la siguiente manera:

$fecha = "DATE '" . date("Y-n-j H:i:s",time()) . "'";

Nota: notese que hay que concatenar “DATE ‘” a nuestra fecha, para que a la hora de introducirlo a la BD lo reconozca como tal.

-FechaGMT: Lo mismo que el anterior, incrementando +2 horas. Todo esto de las fechas, depende totalmente de donde nos encontramos.

$fecha_gmt = "DATE '" . date("Y-n-j ",time()) . (date("H",time())+2) . date(":i:s",time()) . "'";

-Contenido y Titulo: Este par de campos, sencillamente son lo que escribimos de titulo y contenido, por lo que no hay que tratar demasiado.

-ID parseada: Como comenté anteriormente, no es muy dificil de conseguir, simplemente seria concatenando una constante a la ID:

$id_parseada = "http://delanover.com/test_wordpss/?p=" . $id;

-Titulo parseado: Esta la he dejado para el final ya que es la más largilla de hacer, para la cual he creado una función específica. En principio, al ser el enlace, lo unico que puede contener son letras (quitaré las tildes), numeros, guiones bajos y guiones. Por lo que haré lo siguiente:

$titulo_parseado = parsear_titulo($titulo);

function parsear_titulo($titulo){
  //pasamos a minusculas y cambiamos caracteres
  $titulo = strtolower($titulo);
  $titulo = str_replace(" ", "-", $titulo);
  $titulo = str_replace("á", "a", $titulo);
  $titulo = str_replace("é", "e", $titulo);
  $titulo = str_replace("í", "i", $titulo);
  $titulo = str_replace("ó", "o", $titulo);
  $titulo = str_replace("ú", "u", $titulo);
  
  $caracteres_permitidos = array("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "ñ", "o", "p",
  			"q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "1", "2", "3", "4", "5", "6", "7", "8", "9",
  			"0", "_", "-");
  			
  //Si nos encontramos con un caracter no permitido (que no salga en el array anterior), lo eliminamos
  for($a=0;$a

3.1.3.-La tabla wp_postmeta

Afortunadamente para nosotros, el tratamiento de esta tabla será todavia más sencillo, y limitaré mi explicación por no tener demasiado que ver con el tema principal.

Explicaré brevemente los detalles dentro del código fuente:


//Obtenemos el ID maximo
$consulta_metapost = "SELECT MAX(meta_id) from `wp_postmeta`";
$respuesta_metapost= mysql_query($consulta_metapost);
if($resultado=mysql_fetch_array($respuesta_metapost))
	$_id = $resultado[0]+1;

//Introducimos el ID recogido, el ID anterior que teniamos del post, y dos valores constantes
$consulta2 = "INSERT INTO `wp_postmeta`(`meta_id`, `post_id`, `meta_key`,
`meta_value`) VALUES ($_id,$id,'_edit_last',1)";
$resultado_consulta2 = mysql_query($consulta2);
	
$_id++; //Aumentamos el ID ya que vamos a insertar otra fila
//Obtenemos un valor que meteremos, compuesto por el tiempo y el ID del autor
$meta_valor = time() . ":1";
$consulta3 = "INSERT INTO `wp_postmeta`(`meta_id`, `post_id`, `meta_key`,
`meta_value`) VALUES ($_id,$id,'_edit_lock','$meta_valor')";
$resultado_consulta3 = mysql_query($consulta3);

3.2.-Implementando la seguridad en nuestra aplicación

Muy bien, con todo esto ya deberia de funcionarnos el sistema, pero claro, falta lo más importante, implementar la seguridad con ese hash dinámico que generamos.

Lo más sencillo es crear una función que nos devuelva un valor booleano, en función de si el hash introducido es verdadero (entonces se ejecuta) o falso (devolviendo un error).

function comprobar_hash($hash){
$hora = date("H", time())+6; //Obtenemos la hora (la adecuamos en función de la hora del servidor a la hora de nuestro pais)
$dia = date("j", time()); //Obtenemos el dia

//Nuestro salt
$cadena = "lipman";

//En función del dia, generamos un resultado u otro
switch(date("N", time())){
  case 1:
  $nueva_cadena = "Q" . $cadena[0] . $cadena[1] . $cadena[2] . $cadena[3] . $cadena[4] . $cadena[5];
  break;
  case 2: $nueva_cadena = $cadena[0] . "W" . $cadena[1] . $cadena[2] . $cadena[3] . $cadena[4] . $cadena[5]; break;
  case 3: $nueva_cadena = $cadena[0] . $cadena[1] . "E" . $cadena[2] . $cadena[3] . $cadena[4] . $cadena[5]; break;
  case 4: $nueva_cadena = $cadena[0] . $cadena[1] . $cadena[2] . "R" . $cadena[3] . $cadena[4] . $cadena[5]; break;
  case 5: $nueva_cadena = $cadena[0] . $cadena[1] . $cadena[2] . $cadena[3] . "T" . $cadena[4] . $cadena[5]; break;
  case 6: $nueva_cadena = $cadena[0] . $cadena[1] . $cadena[2] . $cadena[3] . $cadena[4] . "Y" . $cadena[5]; break;
  case 7: $nueva_cadena = $cadena[0] . $cadena[1] . $cadena[2] . $cadena[3] . $cadena[4] . $cadena[5] . "U"; break;
  default:
  	$nueva_cadena = "error";
  	
}

//Obtenemos la cadena formada y la pasamos a MD5
$resultado = "///" . $dia . $nueva_cadena . $hora . "///";
$resultado = md5($resultado);

//Finalmente comprobamos si coincide con el hash pasado por parámetro
if($resultado==$hash)
	$final = "Verdadero";
else
	$final = "Falso";

return $final;
}

3.3.-Conclusión y funcionamiento final

Nos falta diseñar la página de "login" en la que tendriamos que recoger el titulo de la entrada, el contenido, y el hash en MD5. Este hash en MD5 recordemos que es nuestra contraseña aleatoria pasada a MD5. Un apunte respecto a esto: imaginemos que nos están esnifando la red y tal. Vale, usamos este sistema y todos felices, pero si a la hora de crear nuestro hash MD5 (para que posteriormente lo valide) usamos una herramienta externa en una página web, nos esnifan esto, y no tiene mucho sentido.

Para ello, recomiendo que tengais un programa o algo, ya que desde la consola de Windows, a diferencia de la Linux, no se puede obtener el MD5 de una cadena. Si no, si teneis un servidor web instalado, lo haceis desde ahí usando la función de PHP "md5".

4.-Códigos finales completos

crear.html




Metemos el hash

Titulo

Contenido

create.php

error
"; function parsear_titulo($titulo){ //Pasamos a minusculas y quitamos las tildes $titulo = strtolower($titulo); $titulo = str_replace(" ", "-", $titulo); $titulo = str_replace("á", "a", $titulo); $titulo = str_replace("é", "e", $titulo); $titulo = str_replace("í", "i", $titulo); $titulo = str_replace("ó", "o", $titulo); $titulo = str_replace("ú", "u", $titulo); $caracteres_permitidos = array("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "ñ", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "_", "-"); //Si encontramos un caracter que no está en el array de permitidos, lo eliminamos for($a=0;$a

Your WordPress.com Account Xxxx Is Not Authorized to View the Stats of This Blog y No disponible por mantenimiento programado. Vuelve a comprobar el sitio en unos minutos

Si teneis un blog de WordPress y teneis instalado el plugin para las estadísticas de las visitas llamado WordPress Stats es probable que os de un error de ese tipo.

Solución al error “X is not authorized to view the stats of this blog”.
-Desactivar ese plugin
-Instalar Jetpack


Jetpack

Para instalar Jetpack nos vamos a Plugins -> Añadir nuevo, y en el buscador, introducimos el nombre. Si no teneis cuenta en wordpress.com os pedirá hacerla, ya que para activar el plugin pide login.

Nota: Jetpack requiere WordPress 3 en adelante, lo mejor será actualizar a la última versión, no sin antes hacer una backup por si algo falla. Para hacer la copia de seguridad, nos vamos a Herramientas -> Exportar y nos exportará en un documento XML las entradas, comentarios, y demás. Y para actualizarlo, simplemente dadle a donde os dice que ya hay una versión posterior a la que usais, o desde Herramientas y por ahí teneis una versión de actualizar, o incluso desde las nuevas versiones, en la parte de Escritorio tenemos un apartado exclusivo para actualizar.

Otro error derivado de actualizar: No disponible por mantenimiento programado. Vuelve a comprobar el sitio en unos minutos.
Después de actualizar a wordpress 3.1 (ya era hora, porque usaba el 2.9.1, aunque era feliz con esa versión), me apareció para actualizar otro plugin que tenia yo para la sintaxis de códigos, WP-Syntax y tras intentar actualizarlo y darme un error relacionado con el directorio public_html.

A cualquier dirección del blog (incluso las de administración) a las que iba, me aparecía el mensaje “No disponible por mantenimiento programado. Vuelve a comprobar el sitio en unos minutos“.

Si esto ocurre, no alarmarse! Simplemente entramos desde un cliente FTP como Filezilla a nuestro servidor web (o desde el que nos proporciona nuestro servicio de hosting) y eliminamos el archivo .maintenance situado en el directorio raiz donde tenemos instalado nuestro blog.

Saludos, lipman