Ahorrando ancho de banda con sprites y CSS

Los sprites son en realidad simples imágenes que contienen varias imágenes y se muestran solamente una a una usando técnicas para recortarlos. Estos se han venido usando desde los primeros videojuegos para realizar animaciones, por ejemplo o para guardar iconos y dibujos. Eran tiempos en que la memoria era un lujo y las velocidades bajas por lo que poder guardar los sprites en el buffer de la memoria de video y así usarlos rápidamente era una ventaja.Sprites

Ahora, esto ya no suele ser un problema: tarjetas gráficas con cientos de megabytes de memoria, motores de cálculo 3D, montones de texturas. No es un problema por lo menos para los juegos pero en Internet el ancho de banda sigue siendo un lujo. No siempre para el usuario sino también para el proveedor de contenidos. Con los sprites se pueden conseguir varias cosas:

  • Reducción del peso total de una página web, con el consecuente ahorro de tiempo de descarga y de coste del alojamiento (Ahorrando 10kb por visita, si hay 1.000.000 de visitas mensuales son unos 10Gb, que no es algo despreciable)
  • Reducción del número de peticiones HTTP, con, de nuevo, ahorro del tiempo de descarga, reducción de la carga del servidor. Con un servidor menos potente se pueden servir más páginas.

Evidentemente si no tenemos cientos de miles de visitas, todo este ahorro en realidad no es para tanto, pero cada vez son más los internautas y es algo (y será) a tener muy en cuenta.

La técnica básica es jugar con las propiedades CSS  de width y height para recortar lo que muestra la imagen que se indica en el background y además usar las propiedades de background-position para desplazar el sprite y mostrar la zona que se desea ver. En A List Apart, como siempre tienen un artículo interesante sobre el tema.

Un caso real, explicado por los autores se puede encontrar en el rediseño de Google Code, donde se usaron sprites para las imágenes de la primera página, de modo que se descargasen todas a la vez y así ahorrar llamadas. Desde luego estos de Google son los genios del ahorro.

También existe un interesante libro sobre el tema, aunque mucho más extenso: High Performance Web Sites de Steve Sounders, empleado ex-empleado de Yahoo! (ahora en Google y artífice de las mejoras comentadas) que se dedica exclusivamente a este asunto. Además hay tiene una pequeña pero jugosa web sobre el tema.

Finalmente, si queréis probar las mieles de los sprites con vuestras propias imágenes existe el Generador de Sprites CSS online, aunque siempre será mejor usar algun editor de imágenes para estos menesteres.

Miniguía de Live HTTP headers

El otro día comenté de pasada esta excelente y útil extensión de Firefox. Como todo, cada uno encuentra su uso particular a cada herramienta trabajando en el desarrollo de aplicaciones web resulta práctico y se echa de menos en otros navegadores. Aunque hay otras opciones, como Wireshark o WebScarab, no son tan sencillas ni se integran tan bien con el navegador.

Al instalar la extensión, aparece una nueva opción en el menú de Herramientas llamada Live HTTP headers. Al abrirla se ve algo parecido a esto:

Live HTTP headers main screen

Directamente aparece la utilidad capturando cabeceras hasta que se desmarque la opción de Capture. Arriba hay 4 pestañas que veremos con más detalle y abajo un botón Save All… para guardar un archvo de texto con todos los datos recopilados, otro Replay… para repetir un bloque y después Clear y Close para limpiar y cerrar.

La pestaña Generator nunca la he usado. Se supone que sirve para hacer un listado detallado de todos los accesos y luego pasarlo a alguna herramienta que genere tests de carga a partir de los datos. La otra pestaña de la que se puede prescindir es About, aunque está bien saber quién hay detrás de todo esto.

La pestaña de Config muestra algunas opciones como la configuración de los POST, que nunca he tocado pero es para no leer las peticiones de este tipo, leerlas rápidamente, completas o limitadas a 1k. Supongo que para depuración de envío de grandes cantidades de datos puede ser útil limitar de algún modo. Se puede elegir si usar una CSS para mostrar más bonito el listado, si abrir en una pestaña y opciones de filtrado para evitar capturar cierto tipo de peticiones. Finalmente se puede añadir a la barra lateral. En cualquier caso, las opciones por defecto suelen bastar.

Pero entrando en materia, lo útil es la inspección de cabeceras, es decir, la pantalla principal y ese pequeño botoncito donde pone Replay. Muchas veces me he encontrado con protecciones de datos por javascript que, de acuerdo, con un navegador normal a un usuario normal, funcionan bien y evitan errores. Pero ni todos los navegadores son como querríamos ni todos los usuarios tienen las mismas intenciones.

Pongamos por ejemplo un simple formulario de registro. Se comprueba por javascript que los datos son correctos, no de dejan el nombre en blanco, ponen una contraseña, ponen un mail aparentemente correcto, aceptan el aviso legal y todo lo que el cliente desee. Luego estos datos deben validarse en el servidor, por lo menos para evitar sorpresas, pero ¿cómo se puede probar eso si solamente se puede entrar por el navegador? Una opción es desactivar javascript, pero igual ni siquiera funciona el formulario, así que entra en juego la opción de repetir una petición de la herramienta que nos ocupa.

Formulario simple

Simplemente hay que capturar el momento en que se pulsa enviar y capturar el flujo de información. En algún sitio habrá una petición de POST (si es GET, probar las cosas es tan sencillo como ir modificando los parámetros y para eso no se necesita ninguna herramienta). Se situa el cursor sobre la petición y se pulsa el botón de Replay…

Live HTTP headers replay screen

Y mágicamente aparece un desglose de la petición. Por un lado las cabeceras arriba (que son completamente modificables, de hecho he cambiado las URL y información sobre las cookies que aparecía) y abajo el contenido del POST.

Un ejemplo podría ser dejar el nombre en blanco. Solamente hay que borrar Prueba después de tNombre= todo lo que hay hasta & y es como si estuviera en blanco. Al hacer esto, abajo hay un contador de Content-Length que es sumamente importante ya que ese dato se ha de copiar al valor de arriba. En este caso, antes era 94 y al borrar Prueba se queda en 88, por lo que hay que modificar el valor 94 de la cabecera por 88 para que funcione, de lo contrario cosas extrañas van a pasar.

Hecho esto, simplemente hacer click sobre Replay a se volverá a lanzar la petición en el navegador y mostrará el resultado de esta. En el mejor de los casos nos indicará que pongamos el nombre y en el peor aparecerá un bonito error. Suerte. (En mi caso no ha pasado nada especial pero sospecho que el correo que contacto se ha enviado a algún sitio, mañana veré).

Y eso es todo, espero que resulte de utilidad… y ¡no hagáis maldades!

Wapiti, auditoría web sencilla

WapitiDe un tiempo a esta parte habían empezado a llegar a una cuenta de correo que tenemos configurada para recibir los informes de error de las distintas aplicaciones web una serie de errores bastante sospechosos. Eran todo URLs con cadenas de texto en parámetros numéricos que hacían fallar la página. Esto se producía evidentemente por nuestra pobre comprobación de los parámetros que producía que en algunos casos el valor numérico en cuestión llegase tal cual a una consulta en la base de datos, fallando estrepitosamente.

Lo curioso del caso es que solamente ocurría en algunas webs concretas y siguiendo siempre el mismo patrón: claramente usaban una herramienta que buscaba posibles vectores de inyección SQL o similares. Era un mecanismo bastante burdo pero seguro que en algún sitio algo encontraron. Además las IPs eran variadas así que posiblemente se debía a la aparición de alguna herramienta en algún foro de script kiddies.

Y me dije: yo quiero hacer lo mismo. Así que me puse a buscar y encontré algunas herramientas, siendo la más interesante Wapiti, un fuzzer sencillo hecho en Python con las siguientes funcionalidades:

  • Detección de errores en la gestión de ficheros (fopen, includes…)
  • Inyección SQL
  • Inyección XSS
  • Inyección LDAP
  • Ejecución de comandos (eval(), system()…)
  • Inyección CRLF (HTTP splitting)

Además muestra un mensaje de alerta cada vez que se produce un error 500, con lo que es ideal para analizar aplicaciones hechas en ASP (ehem).

Así pues hice un testado exhaustivo con esta herramienta a nuestro código base y la verdad que encontré cosas interesantes, como un error de lo más ingenuo que daba paso a un bonito ataque XSS o unas cuantas entradas sin proteger que proporcionaban posibles ataques SQL jugando con los parámetros. Otros errores no eran tan evidentes y se basaban en jugar con los datos enviados por POST, que por cierto para las pruebas encontré imprescindible la extensión Live HTTP Headers que permite ver el tráfico HTTP y luego repetir una llamada modificando las cabeceras (con calculadora automática del Content-Length, afortunadamente).

He de decir que esto evidentemente no es una herramienta de revisión de código pero si me tengo que poner a revisar toda nuestra base de código antiguo apaga y vámonos. Más vale un poco de protección que ninguna protección, por lo menos la valla un poco más alta que la del vecino.

Posiblemente aun queden muchos agujeros que tapar pero en general ya solemos usar protección para todos los parámetros que entran como comprobar que los números son valores numéricos y que las cadenas de texto son como deben ser y no tienen gato encerrado (ver el XSS FAQ para un método bastante competo de protección).

En resumen, que a lo largo de los años se ha descuidado muchas veces la seguridad de aplicaciones web (las prisas, la poca importancia atribuida, el desconocimiento, etc) y cada vez hay más y mejores técnicas para sacar provecho de nuestros fallos, por lo que cualquier comprobación no está de menos.

Actualización 08/03/2008: Kriptópolis se hace eco de un artículo interesante llamado “El mito de la seguridad web total”. Interesante lectura.