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

Qubes OS: El sistema operativo que apunta a la seguridad del usuario

Desde hace tiempo quería dedicar una entrada a esta distribución tan especial de Linux, cuyo objetivo principal es la seguridad. Mencionaré lo básico para su entendimiento de forma breve, y posteriormente (cuando acabe ya los exámenes) intentaré hacer un review instalándomelo en un laptop.

1.-Introducción

Qubes OS, fue presentado hace unos meses, cuyo video se encuentra en youtube (http://www.youtube.com/watch?v=0pPf1F1RGF8). Es un sistema operativo basado en Linux, cuyo objetivo apunta a ser el mejor en lo que a seguridad se refiere. Se basa en el aislamiento. Esto quiere decir que usa diferentes máquinas virtuales, en las que producen distintos procesos, de tal forma que, si un atacante vulnera una fallo de seguridad, por ejemplo, en un navegador, al estar situado en una máquina virtual aislada, no compromete al resto del sistema.

Esta distribución, protege de manera diferente cada máquina virtual, dependiendo de la importancia de cada una, ya que no requeriremos la misma seguridad en un navegador que estamos leyendo la Wikipedia, que uno en el que estemos consultando nuestro banco o comprando.

2.-Arquitectura

Qubes OS está diseñado para que, a pesar de usar diferentes máquinas virtuales, aproveche los recursos de forma eficiente. Este sistema divide las máquinas virtuales en dos grandes grupos. Por un lado, las AppVMs son las que se refieren a aplicaciones del usuario, como navegadores o clientes de correo. Por otro, se encuentran las SystemVMs, que son las que proveen al sistema de servicios, tales como la conexión a Internet o el almacenamiento.

2.1-Las AppVMs

Como dijimos anteriormente, son máquinas virtuales independientes que están basadas en Linux que alojan las aplicaciones del usuario.

2.2-El dominio de Red

Al igual que en otros sistemas típicos, como Windows o Linux, el núcleo del código de red se encuentra en el Kernel del sistema. Si se encontrase un bug en esta zona, en un sistema tradicional significaría el comprometimiento entero del sistema, sin embargo, la arquitectura de Qubes limita ese ataque, quedando comprometida solo la máquina virtual en la que se encuentra, que no tendrá demasiados privilegios.

2.3-El sistema de almacenamiento

Al igual que con el dominio de red, el sistema de almacenamiento (que incluye tanto unidades internas como el disco duro, y externas, como pendrives o CDs/DVDs) se encuentra en una máquina virtual poco privilegiada, de tal manera que estariamos a salvo de potenciales virus alojados ahí.Además, Qubes incluye una protección encriptográfica que proteje los archivos del sistema, por lo que en el peor de los casos, un atacante podría hacer el sistema inarrancable, pero no podría acceder ni instalar un virus en el sistema para robar información.