Di algo inteligente o van a pensar que eres un tonto! - Homero Simpson

CGI::Aplication el fino arte de hacer web perl-style

Perl es una maravilla en cuanto a robustes y disponibilidad de módulos para hacer lo que sea, no solo es posible analizar logs al más puro estilo geek, sino que se pueden hacer cosas como “parsear” un archivo de excel para importarlo a una base de datos, o crear sitios web con todas las características que necesitan.

Para crear aplicaciones Web, Perl tiene un monton de opciones, desde hacer un CGI a mano que haga toda la magia de entender los headers, extraer los argumentos de GET y POST, y desplegar HTML intercalado en el código, al más puro estilo de PHP (porque lo heredó de Perl), sino que existen muchos módulos (CPAN) que nos ayudan a hacer las cosas de manera más ordenada y mantenible.

En esta nota quiero abordar la utilización de un módulo muy robusto, casi podríamos decir que es realmente un Framework de desarrollo web, aunque a mi no me gustan los frameworks,  uno no deja de admirar la simplicidad con la que se puede crear una simple paginita web siguiendo bastante fielmente algunos de las más famosas “mejores prácticas”, como el MVC, el uso de capa de abstraccion en la base de datos, y sobre todo el crear código seguro contra ataques de injeccion SQL y XSS.

Para empezar vamos a plantear el clásico ejemplo de crear un blog muy sencillo, que tenga autenticación, guarde en mysql, y nos permita mandar noticias y poner comentarios en ellas, nada exótico.

Empezemos por crear una base de datos:

mysql> CREATE DATABASE perlblog;
Query OK, 1 row affected (0.09 sec)
mysql> GRANT ALL privileges ON perlblog.* TO blog@localhost IDENTIFIED BY `20y1337` ;
Query OK, 0 rows affected (0.27 sec)
mysql>

Necesitamos varias columnas, una para los datos del usuario (user), otra para las noticias (news), y otra para comentarios (comments), luego veremos si se requiere algo extra.
mysql> CREATE TABLE user (id int UNSIGNED NOT NULL AUTO_INCREMENT, login varchar(50) UNIQUE, password char(32), name varchar(150), created datetime, last_log datetime, PRIMARY KEY (id));
Query OK, 0 rows affected (0.21 sec)
mysql> CREATE TABLE news (id int UNSIGNED NOT NULL AUTO_INCREMENT, title varchar(254), summary tinytext , news text, posted datetime, last_edit datetime, user_id int UNSIGNED NOT NULL DEFAULT 1, PRIMARY KEY (id));
Query OK, 0 rows affected (0.43 sec)
mysql> CREATE TABLE comment (id int UNSIGNED NOT NULL AUTO_INCREMENT, title varchar(254), comment text, posted datetime, last_edit datetime, user_id int UNSIGNED NOT NULL DEFAULT 0, name varchar(255), email varchar(255), PRIMARY KEY (id));
Query OK, 0 rows affected (0.09 sec)

El la columna del usuario al menos nos hace falta su correo electrónico, agreguémoslo:

mysql> ALTER TABLE user ADD COLUMN email varchar(255) after password;
Query OK, 0 rows affected (0.18 sec)
Records: 0  Duplicates: 0  Warnings: 0

Ya estamos listos para empezar, posiblemente lo primero que queremos hacer es poder insertar una noticia, así que vamos a crear una paginita de captura. Para esto necesitamos crear el ambiente inicial:

package Blog;
use strict;
use warnings;
use base 'CGI::Application';
use CGI::Application::Plugin::AutoRunmode;
use CGI::Application::Plugin::DBH (qw/dbh_config dbh/);
sub cgiapp_init {
my $self = shift;
$self->dbh_config("dbi:mysql:perlblog", "blog", `20y1337`);
}
sub news_list : StartRunmode {
my $self = shift;
my $q = $self->query();
my $sth = $self->dbh->prepare("SELECT * FROM news ORDER BY posted DESC");
$sth->execute();
my @rows;
while ( my $row = $sth->fetchrow_hashref ) {
push(@rows, $row);
}
return @rows;
}
1;

Con esto estamos armando el ambiente más mínimo posible, estamos creando un paquete que va a contener todas las rutinas que requiere la aplicación, y estamos usando uso de CGI::Application que será nuestra base para hacer prácticamente toda la programación.

La subrutina “cgiapp_init” es la encargada de inicializar todos los requerimientos, que por lo pronto solamente es la conección a la base de datos, para lo cual estamos usando un plugin para CGI::Application que se llama CGI::Application::Plugin::DBH, en síntesis es meterle la capa DBI directamente nuestro “framework”.

Para poder ejecutar lo que llevamos de aplicación tenemos que crear un script que ejecuta el paquete, lo cual es bastante sencillo, y no volveremos a tocarlo una vez escrito lo siguiente a “index.pl”:

#!/opt/local/bin/perl -w
use strict;
use warnings;
use Blog;
my $app = Blog->new();
$app->run();

Noten que estoy usando un path para perl medio extraño, el 99% de los casos se usará el estandar “#!/usr/bin/perl -w”, yo uso ese pq me gusta más trabajar con el perl instalado por macports, el perl nativo de Mac OS X está medio chafita en algunos aspectos y prefiero algo más estandar, con suerte casi nadie que lea este post va a desarrollar sobre Mac :)

Una vez creados estos 2 archivos en el directorio de los “cgi-bin”, entonces visitamos en el navegador algo como http://localhost/cgi-bin/index.pl y vamos a ver un número entero, posiblemente cero, que será el número de renglones que existan en la columna “news” de la base de datos.

Con esto estamos aprendiendo un truco útil cuando estemos usando CGI::Application, siempre que quieras debugear algo, puedes poner un return de la variable que sea interesante, funciona como el típico “print” para debuguear, la diferencia es que el return termina en ese punto la ejecución de la rutina, y nos devuelve a la pantalla del navegador solamente el valor de dicha variable. Podríamos haber puesto “return ‘hola mundo’”, pero no sería tan divertido.
Ahora insertemos un renglon en “news”, ejecutando:

INSERT INTO news VALUES (NULL, 'titulo 1', 'estes es un resumen', 'y esta es la noticia extendida', now(), now(), 1);

Si recargamos la página en el navegador veremos un “1″, nada excitante, pero podemos meterle más emoción retornando “$rows[0]{‘title’}” en vez de “@rows”, con lo cual ahora veremos un excitante “titulo 1″ !!! solo estamos demostrandonos a nosotros mismos que “podemos interactuar con una base de datos de verdad”.

Ahora vamos al paso de presentar esa información en un formato decenton, vamos a crear una plantilla en HTML muy sencilla, solo para llenar el requisito, una vez entendido todo el proceso, será trivial ir a buscar un diseño gratuito y adaptarlo.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
<head>
<title>[% title %]</title>
</head>
<body>
<p>[% title %]<</p>
</body>
</html>

Tenemos un documento básico de XHTML, pero noten que el título tiene algo extraño ([% title %]), es la presentación del contenido de una variable (title) en el “lenguaje” de Template Toolkit (TT), que será lo que utilizaremos para las plantillas.

El código de nuestro paquete necesita unos cambios ligeros, necesitamos inicializar el sistema de plantillas, y necesitamos decirle a “news_list” que interprete la plantilla “index.html”, vamos a agregar a la rutina “cgiapp_init” esto:

$self->tt_config(
TEMPLATE_OPTIONS => {
INCLUDE_PATH => '/Users/max/templates/',
},
);

Y en “news_list” vamos a sustituir la última linea, la del return, por esto:

my $vars = {
title => 'Soy el blog m&aacute;s 1337',
rows => \@rows,
};
#return $rows[0]{'title'};
return $self->tt_process('index.html', $vars);

Con esto estamos mandandole a la plantilla 2 variables, aunque solo está utilizando una de ellas. Puedes darle “recargar”, “reload”, o como diga tu navegador :) .

Ahora vamos a usar la segunda variable, que en realidad es una ·$&%$&/· referencia a un arreglo que tiene por elementos hashes (o arreglos asociativos), pa su madre no ?? como nota personal, odio las referencias en perl, siempre me confundo en que es una referencia y que no, pero bueno, sigamos.

Queremos cliclar sobre las noticias, pero solo hemos insertado una, inserta unas 2 o 3 más, incluso pueden usar exactamente los mismos datos que la primera, pero a veces ayuda tener una pista de que algo diferente se está haciendo, puedes cambiar el 1 por 2 y luego 3 y luego 4 en el título, y ya con eso tenemos.

Lo que haremos en la plantilla es agregar un ciclo sobre el arreglo “@rows”, no es nada complicado, queda más o menos así:

.....
<p>[% title %]</p>
[% FOREACH row IN rows %]
<p>[% row.title %] ([% row.posted %])</p>
<p>[% row.summary %]</p>
<p><a href="index.pl?rm=view&id=[% row.id%]">Ver noticia</a></p>
....

Con esto ya tenemos el listado de las noticias, ahora falta que podamos ver la noticia en extenso, y agregarle comentarios. Con el siguiente paso vamos a aprender como se definen más actividades a través de un solo módulo.

En el último cambio a la plantilla estamos agregando una liga, la cual contiene 2 variables con sus respectivos valores, “rm”, e “id”, el primero contiene el nombre de la rutina que se desea ejecutar en el módulo “Blog”, en este caso es “view”, y la segunda variable es el dato extra que necesitamos para saber cual noticia desean ver.

Lo que necesitamos ahora es tener la rutina “view”, donde vamos a seleccionar de la base de datos la noticia con el “id” que el usuario desea ver, y la vamos a mostrar en su respectiva plantilla.

La vista es sencilla, solamente tenemos que seleccionar el ID de la noticia que se desea ver, y mandarla a la plantilla:

sub view : Runmode {
my $self = shift;
my $q = $self->query();
my $sth = $self->dbh->prepare("SELECT * FROM news where id = ?");
$sth->execute($q->param('id'));
my $row = $sth->fetchrow_hashref;
my $vars = {
title => $row->{'title'},
row => $row,
};
return $self->tt_process('view.html', $vars);
}

Podemos ver que la vista se define solamente con “Runmode”, esto quiere decir que este módulo solo se va a ejecutar cuando sea llamado explícitamente (rm=view en GET). Y la plantilla puede ser tan sencilla como esto:
<p>[% title %]</p>
<div>
<h1>[% row.title|html %] ([% row.posted|html %])</h1>
<h2>[% row.summary|html %]</h2>
<div>[% row.news|html %]</div>
<p><a href="index.pl">Ir al Inicio</a></p>
</div>

Con esto tenemos la base de la aplicación, pero solo visualmente, todavía no podemos mandar comentarios, ni mandar noticias.
Lo que necesitamos hacer es crear una forma en la plantilla, y recibir en una vista lo que se envía por POST. El modulo “view” cambia un poco, queda mas o menos como esto:
sub view : Runmode {
    my $self = shift;
    my $sth;
    my $q = $self->query();
    if ( $q->param('submit') ) { # POST METHOD, let's save the comment
        $sth = $self->dbh->prepare("INSERT INTO comment (title, comment, posted, last_edit, user_id, name, em
ail, news_id) VALUES ( ?, ?, now(), now(), 0, ?, ?, ?)");
        $sth->execute( $q->param('title'), $q->param('comment'), $q->param('name'), $q->param('email'), $q->param('id')
        );
    }
    $sth = $self->dbh->prepare("SELECT * FROM news WHERE id = ?");
    $sth->execute($q->param('id'));
    my $row = $sth->fetchrow_hashref;
    $sth = $self->dbh->prepare("SELECT * FROM comment WHERE news_id = ? ORDER BY posted DESC");
    $sth->execute($q->param('id'));
    my @comms;
    while ( my $comm = $sth->fetchrow_hashref ) {
        push(@comms, $comm);
    }
    my $num_c = @comms;
    my $vars = {
        title => $row->{'title'},
        num_c => $num_c,
        row => $row,
        comms => \@comms,
    };
    return $self->tt_process('view.html', $vars);
}

Con esto está casi completo el objetivo de este pequeño tutorial, pero falta una parte importante, la autenticación, pero como estamos haciendo todo al menor estilo de CGI::Application, solo tenemos que agregar unas cuantas lineas:
use CGI::Application::Plugin::Session;
use CGI::Application::Plugin::Authentication;
Blog->authen->config(
    DRIVER => [
    'DBI',
    TABLE => 'user',    CONSTRAINTS => {
        'user.login' => '__CREDENTIAL_1__',
        'MD5:user.password' => '__CREDENTIAL_2__',
    },
    ],
    LOGOUT_URL => 'http://latin.example.com/cgi-bin/index.pl',
    STORE => 'Session',
);
Blog->authen->protected_runmodes(qw(view));

y un hacer un INSERT en la tabla “user”:
INSERT INTO user VALUES (NULL, 'admin', MD5('admin'), 'email@example.com', 'Admin', now(), now());

El módulo de autenticación tiene la capacidad de reusar la instancia de DBI, y además nos crea una página de logueo, e incluso redirecciona “inteligentemente” a la página que estabamos tratando de accesar. La información que ponemos en su inicialización es la estrictamente necesaria, la tabla donde están las columnas de usuario y clave, las columnas, el método de “encriptación”, y por supuesto el método que se usará para autenticar, no tiene que ser una base de datos escrictamente, se puede crear un driver personalizado para soportar cosas más exóticas.

Y finalmente un punto importante que debemos notar, el uso de sesión, es necesario que guardemos en sesión los datos de autenticación, o el usuario final va a tener que estar metiendo su nombre usuario y clave con demasiada frecuencia, con solo agregar el módulo de session, y decirle al driver de autenticación que guarde las variables en él, ya tenemos un método de autenticación completo y funcional.

Noten que solo estoy poniendo como protegido al módulo “view”, podemos poner a cualquier módulo protegido, (ej. qw(view list otro_modulo) pero normalmente la lista de noticias nos gusta mantenerla pública, y de hecho normalmente la vista extendida de la noticia también, pero en este caso tenemos en el mísmo módulo tanto la vista extensa, como el agregar comentarios, es una buena idea separar los comentarios en otro módulo, de tal manera que podamos atomizar mejor los privilegios sobre la página.

Obviamente este ejemplo es muy mínimo, y se ve terriblemente mal, porque no tiene ni siquiera CSS, pero todo eso se corrige con la ayuda de un diseñador, los programadores no somos buenos diseñadores, así que pa que le hacemos al cuento? (tip: baja una plantilla gratuita de algun sitio :)

Espero que a alguien le parezca interesante este resumen de cómo usar CGI::Application, la documentación es bastante buena, pero cada módulo está documentado independientemente, y a veces es un poco complicado para alguien (como yo) de encontrarle la hilación, pero una vez que se entiende la base, lo demás es muy sencillo.
Ah !!, y una de las grandes ventajas de usar CGI::Application es que fácilmente se puede montar tanto sobre CGI como en mod_perl, aunque nunca he usado mod_perl :) , pero en teoría no hay prácticamente nada que hacer para que funcione en ambos.

Eso es todo amigos, buen día :)

Tags: , ,

Filed under:perl

La evolución de la web

Recuerdo mis inicios en el interneis, hace como 13 años, cuando empecé a trabajar en la tesis de mi licenciatura, todo era feo, lo mejor era webcrawler y lycos, y en menos de lo que canta un gallo cada búsqueda en ellos nos daba un 90% de resultados relacionados con p0rn.

En aquellos tiempos ni siquiera había estándares decentes de HTML, es más, JavaScript apareció después, y no fue aprovechado para algo productivo hasta bien entrada la década del 2000. Una página bonita era la que tenia buenas animaciones en GIF !!, despues ya finalizando los 90’s, empezaron los frames, y se empezó a popularizar más el contenido multimedia (antes el internet servia para ver fotos y chatear en IRC/BBC), recuerdo que era la neta bajar cortos de películas (Pulp Fiction era mi preferida para buscar), curiosamente el formato MOV era la neta, luego MPEG llegó a ser popular también, hasta que llegó la era de la locura en los formatos de video con RealPlayer sacando el propio, luego M$ haciendo lo suyo, y así sucesivamente.

Recuerdo cuando empezó el royo del DHTML, que ahora conocemos con JavaScript/DOM, ahi fué cuando empezó a ponerse buena la cosa, ya se usaban los lenguajes para algo más que poner un texto movil en las páginas, ya podíamos ver layouts complejos !!, dinámicos, etc. Y bueno, más o menos por la misma época dimos el salto de páginas etáticas a las dinámicas, todo era CGI !!! afortunadamente no me metí a programar para web en esos entonces, que miedo !!, recuerdo que Perl se consolidó como “EL” lenguaje de programación, claro con otros como Java y no recuerdo que más, pero no se tardaron en crear lenguajes realmente amigables, como PHP (aunque ya existian desde hacia algunos años, no se hicieron populares hasta entrado el 2000), python, e incluso ruby, entre muchos otros menos populares, y bueno, no voy a dejar de mencionar cosas propietarias como las versiones de Java que sacó M$, que luego degenerarían (por las demandas en contra) en lo que es .NET ahora.

Pero bueno, ya desde hace unos 5 años a todo mundo le queda claro que el lenguaje no es realmente demasiado importante (sobre todo si no manejas demasiado tráfico), sino las habilidades de los programadores, y de los diseñadores para hacer cosas agradables a la vista, y funcionales de verdad.

De unos 3 o 4 años para acá (la cita aquella famosa del web 2.0), entramos en la transición más relevante que hemos tenido en esta década. Durante bastante tiempo Flash (no lo había mencionado !) acaparó totalmente el tema de las animaciones (los GIF simplemente se movian, pero no podría reaccionar facilmente a la interacción), la verdad es que Flash aportó mucho, pero hizo de la navegación web una experiencia frecuentemente desagradable para muchos de nosotros, no solo se llenaron las páginas de animaciones sin sentido, y menus con movimiento, sino que nos acostumbramos a entrar a una página para ver una barra con un mensaje de “Cargando n%”, que simplemente se volvió una locura, nadie podía hacer una página sin meterle flash, incluso me tocó trabajar (mi primer trabajo !) en una empresa que sucumbía a flash a la menor provocación, hasta recuerdo que hice el core de una aplición que en ese tiempo no era realmente muy factible con JavaScript (era demasiado lento todavía), en fin, una época horrible !!, pero no duró demasiado, porque justo por ahí del 2004-2005 empezó JavaScript a tomar muchisima fuerza (se usaba mucho desde antes) al grado de que dió paso a lo que ahora conocemos como web 2.0, que podría existir sin JavaScript, pero que se enriquece muchisimo de este.

Esta transición fué en 2 areas, una fué el inicio del uso de AJAX (curiosamente un invento genuino de M$ !!), así como la transición de páginas dinámicas a páginas comunitarias (wikis, blogs, foros, etc).

Afortunadamente con las mejoras que se le hicieron a JavaScript, de unos años para acá es dramáticamente menos probable encontrarse con páginas que tengan flash, y con ello no solo la vista mejoró, sino que hemos dejado de sufrir por la inestabilidad que Flash tenía a bien en darle a los navegadores.

También hemos ganado que cada empresa/grupo/organización que hace un navegador de web se preocupe realmente porque respete estándares, no solo de JavaScript, sino de CSS y HTML, hace 10 años era terrible ese problema, y ahora sigue siendo un dolor de cabeza (en gran medida por el MSIE), pero ya no es un problema tan serio, y existen buenas bibliotecas de JavaScript que “corrigen” los problemas de despliegue de MSIE principalmente.

Ahora vivimos en la época en la que es posible crear aplicaciones muy al estilo de lo que estabamos acostumbrado a usar en el escritorio, para ejemplos concretos y fáciles de acceder están todos los servicios de google, pero incluso hay ejemplos muy populares, que seguramente te has encontrado muchas veces en esta misma semana, como los editores de “texto rico” como tinymce, y otros menos populares, que son un muy buen ejemplo de lo que se puede hacer con JavaScript, no solo es una aplicación sencilla y completa para crear texto complejo en HTML, sino que además es totalmente portable es fácil de integrar a cualquier otra aplicación web, y funciona en prácticamente todos los navegadores que tiene las características para soportarlo.

En poco más de una década pasamos de maravillarnos con simples animaciones GIF, a tener una cantidad impresionante de juegos en Flash que hacen real gala de lo que es una animación dinámica, y ahora estamos en un punto en el que seguramente muy pronto va a ser posible crear grandes juegos en 3D a través de bibliotecas estilo OpenGL adaptadas a JavaScript, de tal manera que podamos abrir nuestro navegador, y visitar una página en la que podamos empezar a explorar un escenario de Quake Arena y empezar a disparar a diestra y siniestra.

Al menos eso espero yo :)

Tags:

Filed under:Uncategorized

(Dear lazy web) Alternativas para crear codigo PHP para web de manera segura

Todos los que hacen algún tipo de desarrollo para web saben que los problemas que conlleva están en varios niveles: (in)compatibilidad de los navegadores a nivel de HTML, JavaScript y CSS, problemas de seguridad como XSS (cross site scripting) e inyecciones SQL.

Para todo hay trucos, y por ejemplo la incompatibilidad de los navegadores en cualquiera de los niveles se puede corregir tal vez a un 90% en la mayoría de los navegadores (los 4 mas populares :) , sin embargo, los problemas de seguridad pueden ser incluso mas importantes, o mas preocupantes.

En los últimos días he estado pensando en como podría lograrse un método o conjunto de herramientas (funciones y clases) que permitan escribir código PHP para web sin que sea posible un ataque SQLi o XSS, y sin tener que hacer a mano las validaciones.

Una alternativa es NO USAR PHP, con python/django es posible hacer todo eso y más, pero estoy intentando reescribir una aplicación ya hecha en PHP, que va a seguir siendo mantenida por programadores de PHP (en teoría) y que además tiene un esquema de base de datos ya definido (no es un problema para usar django realmente).

He estado dándole vueltas al asunto durante los últimos días, como es usual perdiendo el tiempo en vez de actuar :) , pero si sería chido conocer una alternativa robusta para hacer las cosas de la “manera correcta™”.

Por lo pronto creo que voy a darle un poco por el lado de django, pero si alguien llega a leer esta nota, y además sabe de una alternativa interesante para PHP (2 requisitos complicados de cumplir) por favor comunícamelo con señales de humo, email, respuesta aquí, o como se pueda.

Tags: , ,

Filed under:Uncategorized, debian