Evitar ataques XSS con PHP Input Filter

Hoy una lección de seguridad en programación web: Evitar que a vuestra página le hagan un ataque por XSS (Cross Site Scripting). Si no sabéis lo que es el Cross Site Scripting… os lo explica mejor la wikipedia y así me centro en el tema (una vez le hice un ataque de estos a la página de un grupúsculo neonazi que redirigía a un vídeo del desfile del orgullo gay en Youtube)

Bueno, hay frameworks como Symphony o CakePHP que ya se encargar de implementar este tipo de seguridad, al igual que también lo hacen ciertos gestores de contenidos. Pero si se da el caso de que no estás usando ninguno, estás haciendo PHP «a pelo», lo mejor será que recurramos a la clase PHP Input Filter. Dicha clase permite filtrar código malicioso ingresado en los formularios para prevenir ataques XSS de manera sencilla, pero tiene la cualidad de no limpiar determinadas etiquetas o atributos si queremos.

Para utilizar esta clase, la descargaremos desde el enlace anterior e incluiremos class.inputfilter.php al inicio de nuestro códgi. Luego crearemos una instancia de la clase InputFilter, entonces podremos filtrar los datos con el método process:

    require_once("class.inputfilter.php");
    $filter = new InputFilter();
    $variable = $filter->process($_POST['variable']);

Esto nos permitiría limpiar de etiquetas la variable enviada por $_POST llamada variable. ¿Y si queremos limpiar todos los campos enviados por el método post? Pues basta con pasarle todo el array:

$_POST = $filter->process($_POST);

Y con eso tendríamos el array $_POST limpio de etiquetas y securizado.

Pero ¿y si queremos permitir ciertas etiquetas? Imaginemos que desde un input text metemos texto formateado, y nos interesa que sí puedan subir saltos de línea, texto en negrita o cursiva. Pues muy sencillo también, ya que basta con pasar en la creación del objeto InputFilter un array con las etiquetas que queremos conservar como parámetro:

    $filter = new InputFilter(array('em', 'i', 'br', 'b', 'strong'));
    $_POST = $filter->process($_POST);

Y esto se puede llevar más allá, porque también podemos decir que se permitan ciertos atributos de ciertas etiquetas. Por ejemplo, imaginemos que queremos permitir que inserten enlaces junto a su href y a su id, pero ningún atributo más:

    $filter = new InputFilter(array('a'), array('href', 'id'));
    $enlace = $filter->process($_POST['enlace']);

Tras el array de etiquetas pasamos otro array de atributos, simplemente.

Con estos sencillos consejos lograrás que tus formularios sean más seguros en PHP.

Instalar PHPUnit en WAMP

Inciso antes de empezar: Estoy buscando información y cursos de Symphony 2, porque me han recomendado aprender sobre este framework (ya que el destino parece empujarme a trabajar en PHP, si la vida te da limones haz mucha limonada para meter en tu currículum), si alguien puede aportar algo sobre el tema, por favor, en comentarios.

Y seguimos con el tema, instalar PHPUnit en un servidor Apache en Windows (WAMP).

Bueno, habría que empezar por decir ¿por qué usar PHPUnit? Pues para poder realizar tests automatizados sobre nuestro código PHP de manera eficiente. ¿Cómo lo instalamos en nuestro servidor? Bueno, todo es ir en cómodos pasos:

NOTA: En el artículo original lo hacíamos con PEAR, pero se le quitó el soporte hace años. Editamos para ver cómo hacerlo con PHAR:

  • Añade el directorio (en mi caso «c:\wamp\bin\php\php5.3.4») php a tu PATH
  • Descargamos el fichero PHAR desde https://phar.phpunit.de/phpunit-7.phar (Ojo, puede cambiar la versión con el paso del tiempo) y lo guardamos como C:\bin\phpunit.phar
  • Después ejecutamos el siguiente comando:
C:\Users\username> cd C:\bin
C:\bin> echo @php "%~dp0phpunit.phar" %* > phpunit.cmd
C:\bin> exit
  • Abre un nuevo prompt en línea de comados y comprueba que se ha instalado correctamente con el siguiente comando:
C:\Users\username> phpunit --version

Ok, ahora debería estar funcionando e instalado. Lo mejor es hacer un test de prueba para ver si funciona todo. En este caso una tontería básica de la que sabemos el resultado: comprobaremos que uno es igual a uno.

  class MyTest extends PHPUnit_Framework_TestCase
  {
    public function testUnoIgualUno()
    {
      $this->assertEquals(1, 1);
    }
  }

Ahora desde otro terminal basta ejecutar phpunit MyTest.php y este debería devolverte como respuesta el número de versión de PHPUnit, el tiempo que necesitó para la operación, la memoria consumida y un mensaje como este: OK (1 test, 1 assertion)

Todavía no he profundizado mucho con este software, pero cuando tenga tiempo en el trabajo espero poder ponerme con ello porque facilita mucho la vida el poder realizar tests de forma organizada.

Editores WYSIWYG para tu web

¿Necesitas un editor WYSIWYG para integrar en tu página web? ¿Quieres mandar e-mails con texto enriquecido? ¿Quieres que tus usuarios puedan generar código con texto formateado? Ok, pues estas son algunas de las mejores opciones libres a las que puedes recurrir:

  • TinyMCE: Usado por WordPress, Facebook, Oracle Beehive, IBM Web CMS, Joomla, Episerver… en fin, son nombres que deberían dar confianza al usuario. TinyMCE incluye una enorme variedad de opciones que tú puedes configurar y decidir cuales usar. Es fácil de integrar y tiene una amplia documentación.
  • NicEditDos motivos para plantearse el uso de NicEdit son su ligereza y su fácil integración (sólo requiere dos líneas de código y lo tendrás funcionando). No tiene tantas opciones como el anterior pero son suficientes para el uso habitual del usuario, lo que además hará que para este sea más fácil e intuitivo usarlo.
  • openWYSIWYG: Reconozco que no lo he probado, pero me han hablado de él. Por lo que me han comentado el proyecto está un poco abandonado, así que es posible tener problemas de compatibilidad con las últimas versiones de algunos navegadores.
  • CKEditor: A nivel de funcionalidades implementadas está en la misma liga que TinyMCE, y es el editor utilizado por Drupal. Muy potente y con gran cantidad de opciones. Cuando lo veas parecerá que estás ante el procesador de texto de una suite ofimática.
    CKEditor
    CKEditor

     

En fin, con estos cuatro más o menos ya vemos todo lo que hay en el mercado. ¿Y cómo los implementas? Pues mira la documentación de cada uno. ¿Y cómo recoges los datos? Tranquilo, al procesar el formulario te vendrá como el resultado de un textarea normal, al menos en PHP (las versiones para Java o ASP ya vendrán en su documentación explicadas, digo yo).

Enviar mails con PHPMailer

¿Quieres enviar un mail desde una página de php? Ya sea al usuario, al administrador… la idea es que tu web sea capaz de enviar correos a una dirección de e-mail. Aunque existe la función mail() en php, desde hace tiempo la opción más utilizada es la clase PHPMailer que puedes descargar desde aquí.

Una vez descargado la cuestión es ¿cómo hago para enviar un correo? Bueno, todo es cuestión de configurar un script tal que así:

/*Lo primero es incluir la clase en vuestro php*/
include("metedLaRutaDondePusisteisLaCarpeta/class.phpmailer.php");

/*Obviamente, generáis el mensaje*/
$mensaje="Blablablablabla********"; //puede incluir html, podéis cogerlo de un form...

/*Ahora toca crear una variable de la clase phpmailer*/
$NewMail=new PHPMailer();

/*Y luego añadís todos los parámetros que esta necesita para el envío*/
$NewMail->Host = "localhost"; //El host donde tengáis alojado el correo
$NewMail->From = "correo@del.remitente"; //El correo del remitente
$NewMail->FromName = "Nombre remitente"; //Nombre del remitente
$NewMail->Subject = "asunto"; //El asunto del correo
$NewMail->AddAddress("emailalqueseenvía@asdsd.com","Nombre");//La dirección del receptor del mail
$NewMail->Body = $mensaje; //El mensaje
$NewMail->AltBody = "Relleno Relleno; //relleno

/*Y finalmente llamáis al método de envío*/
$NewMail->Send();

Hay más opciones de configuración, pero esto es lo básico. Entre otras cosas podéis configurar el protocolo de envío (POP, SMTP…), etc… pero con esto pelao debería ir funcionando. Si no, buscáis la documentación y ya profundizáis.

Procesar un formulario con PHPFox (operaciones CRUD)

En los últimos tiempos he estado trabajando con el CMS PHPFox para creación de redes sociales. He de decir que la documentación sobre el mismo es, en el mejor de los casos ambigua y en el peor insuficiente, teniendo que recurrir muchas veces al «voy a ver cómo está hecho aquí, y si se parece lo copio». Por cierto, si no sabes inglés date por jodido que la documentación está toda en la lengua de los inmortales bardos Poe, Shakespeare y Lemmy.

Podría empezar por lo básico, pero está más o menos bien explicado en su documentación (a la que sólo podrás acceder si tienes una cuenta de usuario… de pago, of course), donde te explican más o menos cómo crear tu primer módulo. Por desgracia, tras explicar lo más básico (lo más de lo más, porque esto no está explicado…) se quedan ahí, y no profundizan para acercarse al tema de la persistencia de datos ni a las operaciones CRUD (Create, Read, Update and Delete).

Para el acceso a la base de datos necesitarás al menos tres archivos, ya que PHPFox trabaja sobre la arquitectura MVC: un template (será la vista), un controller (controlador) y un service (el modelo).

Si habéis leído la documentación básica de phpfox ya sabréis en qué carpeta tenéis que meter cada archivo y cómo nombrarlo. Ahora os adjunto un ejemplo de como sería el código del template, seguido de su explicación:

<form method="post" action="{url link='phpfoxexperiment/form'}">
  <input type="number" name="val[orden]" id="orden" placeholder="orden"/>
  <br/>
  <input type="text" name="val[texto]" id="texto" placeholder="texto"/>
  <br/>
  <input type="submit" value="Add" class="button" name="val[add]" /> 
</form>

Por partes; en el action hemos usado esa variable porque así generará directamente un link a nuestra dirección seguido de ese directorio. En cuanto al nombre de los campos como «val[loquesea]» tiene también una explicación: se considera buena práctica no usar en PHPFox las variables de entorno $_GET y $_POST para recoger los datos de los formularios, en lugar de eso utilizamos el método getArray de la clase request, como verás en el ejemplo e abajo del php.

<?php 

class Phpfoxexperiment_Component_Block_Form extends Phpfox_Component 
{     
    public function process() 
    {         	
	if (($aVal = $this->request()->getArray('val'))) // getArray() nos permite recoger indistintamente valores $_GET y $_POST 
	{  
		  		 
		if(isset($aVal['add']) && !empty($aVal['add'])) // miramos si se ha pulsado el botón enviar 
		{  
		    if(isset($aVal['orden']) && !preg_match("/^[[:digit:]]+$/", $aVal['orden'])) //esto es una validación de los datos por expresión regular
		    {
			$this->url()->send('phpfoxexperiment.basic', null, 'Error'); //en caso de que los datos no sean correctos avisa a una página de error
		    }
		    else{
			if (Phpfox::getService('phpfoxexperiment')->add($aVal)) // llamamos al servicio para guardar los datos
			{ 				 
			    $this->url()->send('phpfoxexperiment.basic', null, 'Added with success');	//en caso de éxito lo comunicamos			 
			}else{
     			     $this->url()->send('phpfoxexperiment.basic', null, 'Error'); //en caso de error también
			}
		    }
		  }
		}         		 
	} 
}
?>

Como ves en el ejemplo se invoca al método add del servicio phpfoxexperiment. Ese método es el que hará la inserción en la base de datos. No se trata de un método del sistema, sino de uno creado por mi dentro del servicio. Aquí el código del mismo

<?php 
  
class Phpfoxexperiment_Service_Phpfoxexperiment extends Phpfox_Service  
{ 
    public function getData($iUser)
	
    { 
        return $this->database()->select('e.aescrypt') 
            ->from(Phpfox::getT('experiment'), 'e')
	    ->where('e.orden < 60 and user = '.$iUser)             
            ->execute('getRows'); 
    }
	public function add($aVal){ 
     
        $this->database()->insert( 
          Phpfox::getT('experiment'), 
          array(
            'user'=>Phpfox::getUserId(),
            'orden'=>$aVal['orden'], 
            'aescrypt'=>$aVal['texto'] 			
          ) 
        ); 
	return true;

    } 
} 
  
?>

En cambos casos accedo a la base de datos (MySQL) a través de los métodos de la clase database() de phpfox, que teóricamente ya implementa seguridad contra inyección sql y otros posbiles ataques. El return del final servirá para confirmar que la transacción se ha realizado. En el primer ejemplo se recuperar unos datos (teniendo en cuenta el id del usuario) y en el siguiente se insertan.

En fin, una explicación muy básica, pero que os ayudará a ir empezando a desarrollar módulos para este CMS.

Enviar datos por POST desde php mediante cURL

Habitualmente para mandar una petición HTTP con datos POST solemos utilizar un formulario desde HTML, y enviarlo ya sea de forma nativa o mediante AJAX/Javascript.

Pero la semana pasada me vi con una cuestión delicada en el curro. Tenía que enviar un archivo utilizando un API externa y, como medida de autenticación, tenía que generar una firma que consistía en un hash de los parámetros más una clave secreta. El «secretismo» de la clave me impedía generar dicha firma en el cliente. Aunque la metiera en un campo oculto o la encapsulara dentro del javascript siempre existe un riesgo porque tiene que llegar al equipo del cliente. Por lo que pensé «bueno, alguna forma habrá de hacer esto en el servidor». Y descubrí cURL, una herramienta libre que me solucionaría la papeleta.

El razonamiento para solucionar el problema fue el siguiente: envío los datos por POST de la forma habitual al servidor, a una clase PHP que los maneje. Desde ahí genero la firma con los datos del formulario más la clave secreta (que de esta forma nunca viaja hasta el cliente), los envío al API, recojo la respuesta y la manipulo. Todo fetén.

¿Y como lanzo la petición desde cURL? Aquí te rulo un ejemplo del código:

<?php
//Lo primerito, creamos una variable iniciando curl, pasándole la url
$ch = curl_init('http://loquesea.com/quepachacolega.php');

//especificamos el POST (tambien podemos hacer peticiones enviando datos por GET
curl_setopt ($ch, CURLOPT_POST, 1);

//le decimos qué paramáetros enviamos (pares nombre/valor, también acepta un array)
curl_setopt ($ch, CURLOPT_POSTFIELDS, "parametro1=valor1&parametro2=valor2");

//le decimos que queremos recoger una respuesta (si no esperas respuesta, ponlo a false)
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true)

//recogemos la respuesta
$respuesta = curl_exec ($ch);

//o el error, por si falla
$error = curl_error($ch);

//y finalmente cerramos curl
curl_close ($ch);
?>

Et voilà, petición enviada con datos por POST. Un truquito fácil que te puede ayudar a automatizar tareas o a añadir seguridad a tu página.

Convertir un objeto SimpleXMLElement en un array

Toda la mañana peleando con un API que debería devolverme un array de PHP pero que, cosas de la vida, me devuelve un SimpleXMLElement. Cosillas del PHP y de tener que integrar aplicaciones externas a un proyecto usando sus APIs propias (y, en muchos casos, chapuceras). Tras mucho romperme la cabeza (cosa mala) llegué a la conclusión de que con aquello no había dios que trabajara (o sí, pero necesitaría unos días para aprender a manejarlo) y que mejor me sería pasarlo todo a un array y manipularlo a gusto. Tocó búsqueda por stack overflow y google y aparecieron muchas soluciones.

La mayoría son un truquito tal que así, recorriendo el XML y almacenando sus elementos en un array:

/*Siendo $xml el objeto SimpleXML*/
function xml2array($xml) {
  $arr = array();
  foreach ($xml as $element) {
    $tag = $element->getName();
    $e = get_object_vars($element);
    if (!empty($e)) {
      $arr[$tag] = $element instanceof SimpleXMLElement ? xml2array($element) : $e;
    }
    else {
      $arr[$tag] = trim($element);
    }
  }
  return $arr;
}

Esa es la solución propuesta por php.net, que funciona, sin duda. Pero he visto otra más simple y cortita en Book of Zeus donde lo solucionan con una sola línea:

$xml = json_decode(json_encode((array) simplexml_load_string($string)), 1);

Funciona, lo que no tengo claro es cual cargará menos tu servidor a nivel de rendimiento (eso de codificar como JSON y luego descodificar… mmmmmm, pero a saber). Yo me he decantado por la segunda, que hace el código más legible.

Magento vs PrestaSHOP

Aunque mi experiencia con CMS para tiendas on-line no es muy amplio es habitual tener la duda a la hora de desarrollar una sobre cual utilizar, por lo que creo interesante hablar de estos dos gestores: Magento y PrestaSHOP. De OSCommerce no he visto mucho, pero algún camarada me lo ha puesto a parir por ser demasiado farragoso.

Así que toca hablar de los puntos fuertes de los dos:

Magento: Magento es el Rolls Royce de las tiendas on-line: permite versión para teléfono móvil (no se si PrestaSHOP ya lo permite, pero cuando yo lo utilicé todavía no), organizar nuestra tienda online en varias vistas y sitios web (se decía que PrestaSHOP 1.5 ya integraría esto, pero tampoco lo trabajé con esa versión), tiene una gestión de los atributos de los productos cojonuda, una cantidad de módulos impresionante (eso sí, la mayoría de pago y algunos muy caros) y su integración con ERP’s es muy buena.

PrestaSHOP: Su gestión de estadísticas es muy completa, incorpora infinidad de herramientas de marketing (que en Magento son módulos de pago), es más usable para el usuario (bueno, aquí no necesariamente, pero suele serlo), permite una mejor comunicación entre administradores y usuarios por su CRM ligero y tiene una comunidad de usuarios mayor (clave a la hora de solucionar problemas).

Claro, dicho así uno se pregunta ¿por qué voy a usar PrestaSHOP si Magento es más potente? Ahora llegamos al punto principal. Sí, Magento es mucho más potente pero tiene los siguientes handicaps:

  • Desarrollo: Magento tiene una estructura compleja, requiere «tocar» muchas cosas para hacer modificaciones, tiene una curva de aprendizaje muy pronunciada y requiere que el programador conozca bastante bien el modelo EAV y Zend Framework, y puede llevarle un par de meses dominar Magento. En cambio, cualquier programador PHP puede estar trabajando con PrestaSHOP sólo en un par de semanas.
  • Rendimiento: Si tienes los creo que 15.000 $ que cuesta la licencia de pago de Magento, lograrás que este no le envidie nada a PrestaSHOP en agilidad. Pero como hablamos de las versiones gratuitas, en este campo también barre PrestaSHOP. Se recomienda tener el back office de Magento en un servidor distinto al del resto de la tienda para lograr un rendimiento decente… con esto te digo todo. El cambio, cualquier hosting profesional mueve PrestaSHOP sin problemas.
  • Administración: Ya hemos dicho que Magento tiene más módulos y más opciones en su back office. Pero claro ¿qué implica esto? Mayor dificultad a la hora de aprender a utilizar la tienda. La curva de aprendizaje para el administrador es más pronunciada que en PrestaSHOP.

Entonces ¿cual es la mejor opción? Pues si tienes una tienda pequeña y no esperas que el negocio crezca mucho lo normal, suponiendo que esto implicará que no quieres/puedes invertir mucho en la tienda, tu opción es PrestaSHOP. Si tu negocio ya es muy grande (hablamos de tiendas que oferten una cantidad muy grande de productos y con varios miles de visites al día) entonces tendrás que meter más pasta e invertir en Magento.

Convertir fechas de formato MySQL a español en PHP

Uno de los problemas al recuperar una fecha desde MySQL es que te la devuelve en formato aaaa-mm-dd, el cual tiene su lógica a la hora de hacer comparaciones de fechas, pero es un pelín engorroso y raro mostrarlo así en pantalla. Antaño usaríamos una expresión regular para arreglarlo pero en PHP 5 hay una solución mejor, por lo que he actualizado el artículo (por eso y porque en la solución antigua usaba ereg que actualmente es una función obsoleta), ya que con un objeto DateTime se acaba el mamoneo:

/////////////////////////////////////////////////// 
//Convierte fecha de mysql a español 
//////////////////////////////////////////////////// 
function cambiaf_mysql($fechadb){
//vamos a suponer que recibmos el formato MySQL básico de YYYY-MM-DD
//lo primero es separar cada elemento en una variable
   	list($yy,$mm,$dd)=explode("-",$fechadb);
//si viniera en otro formato, adaptad el explode y el orden de las variables a lo que necesitéis
//creamos un objeto DateTime (existe desde PHP 5.2)
   	$fecha = new DateTime();
//definimos la fecha pasándole las variabes antes extraídas
        $fecha->setDate($yy, $mm, $dd);
//y ahora el propio objeto nos permite definir el formato de fecha para imprimir que queramos       
   	echo $fecha->format('d-m-Y');
}

Como podéis ver la cosa es sencilla. Sobre todo para el tema de insertar fechas desde PHP a MySQL. Porque si vuestra cuita es mostrar en pantalla la fecha formateada, también podéis pedir que MySQL os la devuelva con el formato que queráis. Basta con usar la función date_format() en la query de la select. En el ejemplo vamos a pedir que nos devuelva la fecha con el formato español ya:

SELECT DATE_FORMAT(campo_fecha,'%d/%m/%Y') FROM tabla

Ale, artículo actualizado para vuestro gozo y disfrute.

Instalar complemento de PHP para MongoDB

Como complento a la entra sobre cómo instalar MongoDB en Linux, toca ver cómo se instala la extensión para PHP.

Algunas distribuciones de MongoDB ya traen de por si la citada extensión, así que antes de hacer nada prueba con lo siguiente:

php --re mongo

Si tras esto nos devuelve Extension mongo does not exist es que tenemos que instalarlo. Lo más fácil es usar los repositorios PECL, aunque recuerda que tienes que tener instalada una versión de PHP compatible, porque si no te dará error (si tienes PHP 5 no hay problema, si no instálalo). El código sería el siguiente:

sudo pecl install mongo

##Si necesitas instalar PHP lo puedes hacer con la siguiente instrucción##
sudo apt-get install php5-dev

Ahora, tras reiniciar el servidor basta con volver a ejecutar php –re mongo para comprobar que sí está instalada la extensión.

Con esto ya tienes la extensión para PHP de MongoDB instalada en tu equipo Linux.