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
create.php
//Recuperamos el hash
$hash = $_POST['hash'];
$postear = comprobar_hash($hash);
if($postear=="Verdadero")
{ //Incluimos las variables para conectarnos a la base de datos
include('wp-config.php');
//Recogemos el contenido y titulo del post
$titulo = $_POST['titulo'];
$contenido = $_POST['contenido'];
mysql_connect(DB_HOST, DB_USER, DB_PASSWORD) or die("Error al conectar");
//Obtenemos el ID que del nuevo post
$consulta = "SELECT MAX(ID) from `wp_posts`";
$respuesta= mysql_query($consulta);
if($resultado=mysql_fetch_array($respuesta))
$id = $resultado[0]+1;
//Obtenemos las fechas
$fecha = "DATE '" . date("Y-n-j H:i:s",time()) . "'";
$fecha_gmt = "DATE '" . date("Y-n-j ",time()) . (date("H",time())+2) . date(":i:s",time()) . "'";
//Parseamos el titulo
$titulo_parseado = parsear_titulo($titulo);
$id_parseada = "http://delanover.com/test_wordpss/?p=" . $id;
//Realizamos la consulta principal
$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);
//=====PARTE DOS======\\
//Obtenemos el ID de la otra tabla
$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;
//Realizamos las dos consultas
$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++;
$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);
//Si todo va bien, que muestre el mensaje
if($resultado_principal && $resultado_consulta2 && $resultado_consulta3)
echo "Se ha realizado todo correctamente";
else
echo "Ha ocurrido algún error";
mysql_close();
}
else
echo "
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