Archivo de la etiqueta: apache

Depurando condiciones Rewrite en Apache

Últimamente he tenido que utilizar bastante ModRewrite de Apache en distintos proyectos, algunos de migración y otros de Internet Móvil donde se necesitan ciertas URL’s para que las operadoras lancen sus sitemas de billing.

Con ModRewrite es muy fácil convertir URLs antiguas al formato nuevo en caso de migración (de manera que no pierdes el SEO que tengas). En el caso del billing de las operadoras móviles es todavía más sencillo puesto que simplemente lo usamos para simular una URL premium. Por ejemplo:

http://tudominio.com/premium.php?noticia=3

Se convierte en:

http://tudominio.com/index.php?noticia=3

De este modo no es necesario hacer scripts independientes y separados por operadora sino que todo queda integrado en nuestro framework.

El problema es que es bastante tedioso depurar lo que hace ModRewrite para ver si las condiciones que has escrito son correctas o si algún filtro no se está ejecutando correctamente. Para solucionarlo lo mejor que podemos hacer es decirle a Apache que “logee” todas las decisiones que va tomando sobre nuestros filtros. Tan fácil como esto:

RewriteEngine on
RewriteLog "/logs/rewrite.log"
RewriteLogLevel 4

RewriteCond %{REQUEST_URI} ^/premium.php$
RewriteRule ^/premium.php /index.php

Este sencillo ejemplo nos permite comprobar qué veremos en el log:

213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (3) applying pattern '^/premium.php' to uri '/premium.php'
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (4) RewriteCond: input='/premium.php' pattern='^/premium.php$' => matched
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (2) rewrite /premium.php -> /index.php
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (2) local path result: /index.php
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (2) prefixed with document_root to /var/www/tudominio.com/index.php
213.xxx.xxx.xxx - - [15/Nov/2008:00:48:23 +0000] [tudominio.com/sid#127d7b0][rid#16b8c20/initial] (1) go-ahead with /var/www/tudominio.com/index.php [OK]

Por cada URL que se solicita al servidor, Apache va comprobando las posibles condiciones que has escrito por si coincide con el patrón (matched) o no (not matched). Si la URL cumple los requisitos, la reescribe según se indica en la regla, en caso contrario la deja pasar tal cual. Con toda esta información podemos hacer un completo seguimiento de las reglas que queremos aplicar para comprobar si hay algún error o por qué determinada URL no hace lo que esperamos que haga.

El nivel del log indica la cantidad de información que se guardará, de 0 a 9. A  mayor valor, más información.

Algo muy sencillo pero que pasa desapercibido para la mayoría y acabamos perdiendo muchísimo tiempo probando reglas sin saber qué está ocurriendo en el interior de nuestro servidor web.

Usando certificados SSL de cliente como sistema de autenticación web

A menudo creamos aplicaciones web con un backend de gestión que, por ser también web, exponemos públicamente a cualquiera que consiga averiguar la URL. Habitualmente estos sistemas son de acceso restringido, sólo un pequeño grupo de usuarios lo utiliza.

En escenarios donde tenemos un número de usuarios acotado y se necesita autentificación, se puede utilizar un mecanismo de certificados que aporten mayor seguridad al sistema, de esta manera solo aquellos usuarios que tengan el certificado en cuestión tendrán acceso a la máquina.

Hoy veremos como permitir el acceso a nuestra aplicación a aquellos usuarios que dispongan de un certificado que previamente les habremos enviado mientras que si no lo tienen no podrán acceder de ningún modo. Este método se puede combinar, además, con el tradicional usuario/clave para dar mayor seguridad. Podremos incluso verificar que el nombre de usuario que se intenta utilizar se corresponde con el certificado de usuario que le hemos enviado y no intenta autentificarse con otro.

Conceptos básicos sobre certificados SSL

El método que vamos a ver se basa en certificados SSL. Se utilizan para asegurar la información entre un cliente y el servidor y prevenir escuchas ya que la información viaja encriptada. Ésta es su función y la hace aunque no esté firmado por una autoridad certificadora (CA) oficial o, incluso, aunque esté caducado. Sigue asegurando las comunicaciones.

Los navegadores web reconocen, por defecto, una serie de autoridades certificadoras como Verisign o Thawte, aunque hay muchas más. Puedes verlas todas en las opciones de tu navegador. Pero, ¿qué es realmente lo que hace una Autoridad Certificadora? Firmar. Firma tu certificado SSL asegurando que os pertenece a ti y a tu dominio. Cuando un cliente accede a tu dominio y descarga el certificado SSL, busca dentro de sus certificados de CA‘s si hay alguno que lo haya firmado. Si lo encuentra, acepta tu certificado y no ocurre nada especial, pero si no encuentra la CA lanza un aviso indicando que no se reconoce la autoridad que lo firma. Esto no quiere decir que el certificado no sea válido, lo único que ocurre es que no sabe quien lo firma. Esto significa, por tanto, que tú mismo puedes ser tu propia autoridad certificadora y firmar tus certificados, funcionarán perfectamente y cumplirán su cometido de asegurar las comunicaciones cliente/servidor.

Comercialmente o en sistemas de acceso público en general, no se recomiendan certificados autofirmados ya que el aviso de autoridad de certificación no reconocida generará desconfianza entre tus usuarios, pero en entorno intranet o de paneles de adminsitración es un método ideal.

El servidor puede requerir, además, otro certificado al cliente, de manera que ámbos extremos autentifiquen la comunicación. Esto es precisamente lo que vamos a hacer hoy en este artículo.

Según lo que hemos explicado, los certificados autofirmados son igual de seguros que los firmados por una autoridad certificadora. Como en el ejemplo que estamos viendo estamos asegurando el acceso a nuestra aplicación para un grupo reducido de usuarios, no hay ningún problema en utilizar un certificado firmado por nosotros mismos ya que nuestros usuarios sabrán que no hay ningún problema. Pero esto no es todo, por esta misma razón podemos decir a los usuarios que se instalen el certificado público de nuestra CA, tal y como hacen las CA oficiales, y automáticamente el navegador comenzará a confiar en nuestros certificados ya que, ahora sí, tiene un certificado de una CA que firma los certificados SSL.

Como resumen, nuestro trabjo consistirá en:

  • Crear nuestra autoridad certificadora y su certificado.
  • Crear el certificado SSL para nuestro servidor web firmado por nuestra CA.
  • Crear los certificados de cliente para nuestros usuarios.
  • Habilitar la lectura de los datos SSL desde PHP.

Servidor web SSL

Utilizaremos el paquete Openssl para generar los certificados. Si aún no lo tienes instalado en tu servidor, es el momento. Explicaré de manera rápida como crear crear certificados SSL para asegurar las comunicaciones ya que es el primer paso necesario para añadir certificados de cliente, sin embargo no es el objeto principal de este artículo y hay mucha documentación, googlea un poco 😉 .

Primero creamos el certificado y la clave privada de nuestra autoridad de certificación:

openssl req -x509 -newkey rsa:2048 -days 3650 -keyout CAXplotaKey.pem -out CAXplotacert.pem

Lo más importante de este comando es el parámetro days, ya que no queremos que dentro de un año nos caduque el certificado de nuestra propia entidad. Yo le pongo 10 años. Este comando genera dos archivos, la clave privada con la que firmaremos nuestros futuros certificados y el certificado con la clave pública que instalaremos, si queremos no recibir avisos, en el navegador. Este comando te pedirá algunos datos (nombre de empresa, país…) y, sobre todo, una contraseña. Deberás recordarla cada vez que vayas a firmar un certificado SSL, así que no la olvides. Ya tenemos nuestra CA creada.

Creamos ahora el certificado SSL para nuestro dominio:

openssl genrsa -des3 -out claveprivada.pem 2048
openssl req -new -key claveprivada.pem -out certificado.pem

El primer comando crea la clave privada de nuestro certificado. Te pedirá otra contraseña, esta vez para la clave privada. Recuérdala también.

El segundo comando genera la petición de certificado sobre la clave privada anterior. Te pedirá la contraseña de la clave privada anterior.

En este punto tenemos cuatro archivos, la clave y el certificado de tu CA y la clave y la solicitud de tu certificado SSL. Sólo queda firmar el certificado con nuestra CA y ya podremos utilizarlo.

Para poder firmarlo debemos generar primero un fichero de texto con algunos parámetros de configuración:

cat configservidor.cnf

basicConstraints = critical,CA:FALSE
extendedKeyUsage = serverAuth

Y firmamos el certificado.

openssl x509 -CA CAXplotacert.pem -CAkey CAXplotaKey.pem -req -extfile configservidor.cnf -in certificado.pem -days 3650 -CAcreateserial -sha1 -out certificado-servidor.pem

Ya tenemos un certificado SSL preparado para utilizar en nuestro servidor web. En nuestro caso es certificado-servidor.pem. Vamos a configurar Apache para que lo utilice.

Habrá que editar httpd.conf y añadir

LoadModule ssl_module modules/mod_ssl.so
Listen 443

Es probable que, si no has compilado tu propio Apache y has instalado un paquete precompilado, tengas ya algún ssl.conf. Activándolo tendrás este paso preparado. En mi caso, un CentOS5, sólo hay que incluir /etc/httpd/conf.d/ssl.conf.

Finalmente hay que indicar en el virtual host que quieres asegurar que use nuestro nuevo certificado. Para hacerlo añadimos un nuevo virtual que escuche en el puerto 443 y añadimos las siguientes líneas:

SSLEngine on
SSLCertificateFile /ruta/a/certificado-servidor.pem
SSLCertificateKeyFile /ruta/a/claveprivada.pem

Si ahora reinicias Apache y accedes a tu dominio con https verás que en tu navegador aparece el candado indicando que la información es segura. Verás también que te aparece un aviso de que no se confía en la autoridad certificadora. Veremos más adelante como solucionarlo.

Añadiendo certificados de cliente

Ahora que ya tenemos nuestro servidor web seguro con nuestros certificado autofirmado llega el momento de crear certificados para nuestros clientes de manera que si alguien intenta acceder a nuestra aplicación sin uno de ellos se le prohíba el paso.

Crearemos primero un archivo de configuración con los parámetros que necesitaremos.

cat configcliente.cnf

basicConstraints = critical,CA:FALSE
extendedKeyUsage = clientAuth

Con esto daremos instrucciones de que es un certificado cliente a la hora de firmar el certificado.

Creamos ahora, igual que hacíamos antes, la clave privada y la solicitud de certificado.

openssl genrsa -des3 -out clave-cliente.pem 2048
openssl req -new -key clave-cliente.pem  -out certificado-cliente-req.pem

Como antes, al generar la clave te pedirá una contraseña que deberás introducir después, al hacer la solicitud de certificado. Los datos que te pide esta solicitud, como ocurría antes, los podrás leer posteriormente para comprobar datos o lo que estimes oportuno, así que es importante que prestes atención.

Firmamos ahora el certificado con nuestra CA:

openssl x509 -CA CAXplotacert.pem -CAkey CAXplotaKey.pem -req -in certificado-cliente-req.pem -days 3650 -extfile configcliente.cnf  -CAcreateserial -sha1 -out certificado-cliente.pem

Como véis, el proceso es el mismo para generar el certificado de servidor, pero cambiamos el contenido del archivo de configuración que le indica que es un certificado cliente, no servidor.

Vale, bien, pero ¿no quedamos que es el cliente el que debe instalar el certificado? Sí, ahí vamos ahora. El certificado que acabamos de generar lo debes instalar en tu navegador web, no en el servidor, así que habrá que convertirlo a algún formato que puedan entender. Para esto hacemos lo siguiente:

openssl pkcs12 -export -in certificado-cliente.pem -inkey clave-cliente.pem -certfile CAXplotacert.pem -out cliente.p12

Nos pedirá la contraseña de la clave privada del certificado y nos solicitará otra para el que va a generar. Es importante poner contraseña al certificado final ya que es el que vas a enviar a tus usuarios y pretendes que sólo estos puedan utilizarlo, así que poner una contraseña nunca está demás.

Ya tienes tu certificado cliente.p12. Puedes probarlo tu mismo. Ah no, espera, que no hemos configurado el servidor web para que solicite sí o sí un certificado al cliente. Añade a la configuración SSL de tu virtual host de Apache:

SSLCACertificateFile /path/a/CAXplotacert.pem
SSLVerifyClient require

Y reinicia tu servidor web. Si ahora intentas acceder a tu dominio verás como salta una ventana solicitando un certificado que no tienes. Desde las opciones de tu navegador, busca la sección de certificados SSL y añade un nuevo certificado. Selecciona tu cliente.p12. Te pedirá la contraseña que pusiste al convertirlo a pk12. Ya está. Accede ahora y verás como te aparece el certificado en la ventana de certificiados disponibles y, si lo seleccionas, te deja acceder a tu aplicación.

Revocar certificados

A veces puede ser necesario prohibir el paso con determinados certificados, bien porque el usuario ya no colabora o trabaja contigo, bien porque hay posibilidades de que el certificado haya sido robado, etc. Una opción es, a la hora de crear el certificado, y si sabes de antemano que el usuario lo necesitará pocos días (cuenta de prueba, usuario esporádico o cualquier razón similar), generarlo con una validez limitada (10 días, un mes, etc.), de modo que pasado este tiempo el certificado caduca y el usuario no puede entrar. El problema más importante lo tendrás cuando necesites prohibir el acceso a usuarios con certificados a largo plazo. Para hacerlo debemos crear una lista de revocación de certificados.

En mi caso (CentOS5 y RHEL5) tuve algunos problemas de configuración. Openssl debería de mantener una lista de los certificados que emite y sus números de serie sin embargo tal como hemos generado nuestros certificados no lo hace. Para solucionarlo, creamos a mano la lista.

touch /etc/pki/CA/index.txt

Ahora debemos editar el archivo de configuración de Openssl para reflejar algunos parámetros. Por defecto indica una ruta relativa hacia los directorios donde dejará los certificados, pero no me funcionó bien hasta que no le puse rutas absolutas. Modificamos pues la ruta al directorio y le indicamos donde están la clave privada y el certificado de nuestra CA.

Editar /etc/pki/tls/openssl.cnf

dir             = /etc/pki/CA              # Where everything is kept
certificate     = /path/a/CAXplotacert.pem    # The CA certificate
private_key     = /path/a/CAXplotaKey.pem # The private key

Ahora sí, podemos revocar un certificado de manera tan sencilla como:

openssl ca -revoke certificado-cliente.pem

A partir de la lista con el estado de los certificados debemos crear una lista de revocación que usaremos para indicar al servidor web los certificados que no debe permitir.

openssl ca -gencrl -out recovados.crl

Es posible que al lanzar este comando te apareza otro error relacionado con crlnumber. Esto viene relacionado con lo que os indicaba antes y es simplemente que no existe el archivo de números de serie. Para solucionarlo, simplemente lo creamos:

echo "01" > /etc/pki/CA/crlnumber

Y volvemos a generar la lista de revocación. Cada vez que revoques un certificado deberás repetir esta operación para tener la nueva lista.

Ya sólo falta indicarle a Apache qué certificados no debe permitir. Añadimos a la configuración de nuestro virtual host, en el mismo sitio donde configuramos anteriormente el certificado SSL, la siguiente línea:

SSLCARevocationFile path/a/revocados.crl

Eso es todo, ya tenemos nuestro servido SSL funcionando, nuestra aplicación protegida con certificados de cliente y la opción para revocar certificados.

Comprobando los certificados con PHP

Queremos ir un poco más allá. Queremos que, desde nuestra aplicación, podamos leer el certificado SSL del cliente y comprobar quién es. Seguro que a ti se te ocurren mejores cosas que hacer con estos datos 😉 . Lo primero que tendremos que hacer será configurar Apache para que pase los datos del certificado. En la configuración de tu virtual host añades:

<Files ~ ".(cgi|shtml|phtml|php?)$">
 SSLOptions +StdEnvVars +ExportCertData
</Files>

Ya está. Si ahora haces un print_r($_SERVER) en la aplicación donde tienes instalado tu certificado SSL de cliente verás entre todos los datos algo como:

[SSL_CLIENT_S_DN] => /C=ES/ST=Valencia/L=Valencia/O=Osusnet/CN=blog.osusnet.com/emailAddress=osusENosusnet.com
[SSL_CLIENT_S_DN_C] => ES
[SSL_CLIENT_S_DN_ST] => Valencia
[SSL_CLIENT_S_DN_L] => Valencia
[SSL_CLIENT_S_DN_O] => Osusnet
[SSL_CLIENT_S_DN_CN] => blog.osusnet.com
[SSL_CLIENT_S_DN_Email] => osusENosusnet.com

Tienes toda la información que necesitas, son los datos que se solicitaban al crear la solicitud de certificado. Generando estos parámetros de manera adecuada podrás saber quién entra a tu aplicación a partir del certificado cliente que usa.

Eso es todo por hoy. De una manera muy sencilla hemos añadido un poco más de seguridad a una aplicación web de acceso privado y con un número de usuarios acotado. También hemos aprendido y poco más sobre certificados SSL y hemos visto cómo firmar nuestros propios certificados, igual de fiables que los comerciales. Espero que os sirva de algo 😛 .

Urchin vs. Analytics, la batalla se libra en casa

Webalizer (con su excelente fork continuado awffull) y awstats han tenido históricamente fama de contar visitas y páginas de más, sobre todo desde que Google abrió al público su Analytics y ya nada volvió a ser lo mismo. Hace años tuvimos alguno de nuestros proyectos auditado por OJD y la diferencia con webalizer era mínima, sin embargo las distancias con Analytics son abismales.

Analitycs surge de la compra de Urchin por parte de Google en 2005, otro clásico del software para estadísticas web. Analytics se lanzó primero en beta muy cerrada y bastante después la abrieron completamente al público. Desde el primer día todos nos hemos dado cuenta de las enormes diferencias de datos que había entre lo que contaban nuestros analizadores de logs y lo que contaba Analytics.

Recientemente necesitaba valorar las estadísticas de acceso a distintos proyectos móviles y la única herramienta de la que disponía era el querido webalizer, pero ¡cómo fiase de sus datos!. Analytics no es una opción en este entorno ya que la inmensa mayoría de terminales no soportan Javascript, con lo cual no va a funcionar la contabilización de páginas. Se me ocurrió entonces probar Urchin Software, la última versión del software comprado por Google y que ellos mismos distribuyen y en el que, se supone, se basa Analytics. Aunque el precio de la licencia sea posiblemente prohibitivo para la mayoría, tenemos una versión de prueba de 3 meses, así que dicho y hecho, la descargamos la instalamos y comenzamos a monitorizar nuestros sites móviles. La sorpresa fue mayúscula al cabo de un par de días cuando comprobamos que el resultado es prácticamente el mismo que con webalizer. No nos lo podíamos creer, así que decidimos monitorizar sites también controlados por Analytics para comparar, comenzando por el blog Restaurantes en Madrid. Al cabo de unos días volvimos para comparar el resultado y nos sorprendimos de nuevo. Resultados semejantes a webalizer y completamente distantes de Analytics. Pero ahora la diferencia era enorme, estamos hablando del doble con creces de visitas y páginas que nos muestra Urchin respecto a Analytics.

Urchin (o webalizer, o awstats) se basa en el análisis de logs de acceso del servidor web, mientras que Analytics lo hace a través de Javascript. No cabe duda alguna de que el primer método ha de ser, por fuerza, mucho más creíble y eficaz ya que se basa en peticiones HTTP recibidas de verdad, mientras que el segundo método confía en que el cliente tenga Javascript. Pero ¿qué ocurre con clientes sin Javascript?, más aún, ¿qué pasa con los accesos a feeds?, ¿son visitas no contabilizadas y perdidas? Pues sí. Osea, la herramienta de estadísticas web por excelencia está contabilizando, al menos, un 20% (según he leído en algún sitio) de tráfico menos de lo que en realidad tienes y aún así se ha convertido en el estandar, no lo puedo entender. ¿Los que te leen por RSS no son visitantes también? Se ve que no… La única razón que se argumenta en favor de Analytics es que así no cuenta tráfico proveniente de los robots de los buscadores, pero no dicen nada de los efectos colaterales.

Las críticas que se puedan aplicar a webalizer o a awstats son exactamente las mismas que puedes echar sobre Urchin, con la diferencia de que este último cuesta la nada desdeñable cifra de 3.000 euros. Sin embargo te habrás gastado este dineral para obtener unas cifras nada parecidas a Analytics. Cuando vayas a vender tus espacios publicitarios qué datos vas a enseñar, los de Urchin o los de Analytics, o mejor aún, cuales te van a pedir tus anunciantes?.