HTTP a fondo

De ICEnetX Docs

El protocolo de transferencia de hypertexto (HTTP) es probablemente el protocolo de comunicación más utilizado en Internet. Con el crecimiento de la tecnología y la información hay cada vez más organizaciones y personas que comienzan a basar todos sus servicios en este protocolo. Por eso desde el punto de vista del desarrollo y la seguridad, es esencial entender y saber como funciona HTTP.

Mi objetivo con este texto es que el lector conosca el funcionamiento de HTTP y los elementos que conforman al protocolo. Para que sea capaz de desarrollar aplicaciones seguras que trabajen con HTTP y aprender la manera en las que se pueden manipular las peticiones del protocolo.

Tabla de contenidos

Introducción

Cuando la WWW (World Wide Web) fue desarrollada, se abrió el internet a un nivel público más amplio. Para que un usuario pueda gozar de este servicio necesita un navegador de internet, también llamado Web Browser. Es un programa cliente que se conecta a un servidor Web y muestra al usuario el contenido del servidor de forma gráfica. Esto permite a cualquier usuario navegar en la red, donde puede encontrar una cantidad masiva de información, con un mínimo esfuerzo.

La WWW se compone básicamente de 3 elementos:

  1. URL ­- Uniform Resource Locator (Localizador uniforme de recursos). Es lo que usamos para poder ver una página en nuestro navegador. Con el URL se le indica al navegador que protocolo se va a utilizar para comunicarse con el servidor, en que servidor se encuentra la página y en que parte del servidor se localiza la página.
  2. HTML -­ HyperText Markup Language (Lenguaje HiperTexto de Marcado) Es el lenguaje en el que están hechas las páginas Web. Este lenguaje es interpretado por el navegador y describe como se debe mostrar el contenido de la página, donde se encuentran los elementos multimedia y de que forma están conectados.
  3. HTTP - HyperText Transfer Protocol (Protocolo de transferencia de HiperTexto). HTTP es un protocolo para la distribución de información en HiperTexto. Su principal característica es que permite negociar y transferir a un sistema. HTTP también permite que el sistema que recibe la información la pueda construir de forma inmediata e independiente.

HTTP

Por el rápido crecimiento de usuarios en la Web, el HTTP es el protocolo más usado en Internet. También es el protocolo que usan principalmente los navegadores web. HTTP (normalmente) se utiliza en el puerto TCP 80.

Hablando de versiones del protoclo, la primera versión fue "HTTP/0.9", un simple protocolo para transmitir información a través de internet. Después tuvimos "HTTP/1.0", este protocolo permitía el envío de mensajes en formato "MIME­like". ¿Alguna vez has mirado la cabecera de un correo electrónico? En ella puedes encontrar la información del correo, que no sólo puede ser interpretada por el navegador ya que por su sencillez también es comprensible para cualquier persona.

From: remitente@servidor.com
To: destinatario@servidor.com
Subject: asunto del mensaje
Date: fecha del mensaje

Cuerpo del mensaje.....

Bueno, pues a esto nos referimos con el formato MIME­like. Son los mensajes enviados que contienen la información de los datos a transmitir. A estos datos les vamos a llamar "cabeceras" o "headers".

La desventaja con HTTP/1.0 es que esta versión no toma suficiente consideración en las conexiones por Proxys, el caché, las necesidades para una conexión persistente y hosts virtuales. Además, el crecimiento de una cantidad grande de aplicaciones basadas en HTTP requirió que se hiciera un protocolo que fuera capaz de determinar las capacidades y características de cada aplicación. Así fue como nació HTTP/1.1.

"HTTP/1.1" requiere de más cadenas de mensajes que la versión 1.0 para asegurar un buen desempeño y manejo de las transferencias. Ésta versión permite una amplia variedad de métodos que especifican el propósito de la petición.

Modo de Operación

fig 1
fig 1

Cuando un cliente se conecta a un servidor a través de un DNS (www.google.com), la pila TCP/IP comienza una petición DNS para resolver el IP del host deseado (66.102.7.104). Después se establece la conexión TCP con el servidor y el navegador envía una petición HTTP al servidor. (ver fig 1).

HTTP es un protocolo de petición/respuesta. El cliente envía una petición al servidor especificando método, versión del protocolo, seguido por cabeceras (mensajes tipo MIME­like) que contienen especificaciones de la petición, información del cliente y un posibile cuerpo de datos que procesará el servidor. Después el servidor responde con una línea de estado, incluyendo la versión del protocolo y un código de aceptación o de error, seguido por cabeceras y el cuerpo de la página solicitada.

Después de que todas las operaciones han sido completadas, la conexión es finalizada.

El URL

Para que un cliente pueda realizar una conexión con un servidor se debe de especificar el DNS o dirección IP de dicho servidor. Está dirección HTTP se denomina URL y se compone de las siguientes partes:

[ http:// ] host­name [ : ] puerto [ / ] ruta­absoluta
  1. http:// - Indica que se utilizará el protocolo HTTP.
  2. host­name ­- Aquí va el DNS o IP a donde se conectará el cliente.
  3. puerto - Indica el puerto por el que se conectará, cuando no se indica se toma el 80 por default.
  4. ruta - Indica la ruta (de forma absoluta) del archivo que se pedirá.

Ejemplos de un URL:

http://www.abc.com/index.html
ftp://abc.com     (se especifica que se usará el procolo FTP (file transfer protocol)
http://www.abc.com:8080/

El DNS

Un DNS es un nombre (dominio) que utiliza un servidor para que clientes externos se puedan conectar a ellos. Si los DNS no existieran, en vez de conectarnos a Google por medio de "http://www.google.com" tendríamos que conectarnos especificando su IP "66.102.7.104", y el aprendernos la dirección IP de cada servidor sería un infierno xD. Existen bases de datos que contienen los DNS e IPs correspondientes a cada servidor, de este modo se le hace posible a tu navegador resolver el DNS para saber a dónde conectarse.

Peticiones

Cuando un navegador interactúa con un servidor lo que hace es mandar mensajes HTTP para pedir información, a estos mensajes se les llaman "peticiones" o "requests".

Anteriormente se mencionó que HTTP es un protocolo petición/respuesta, ahora veremos más a fondo que significa.

El proceso que sigue un navegador de Internet cuando se conecta a un servidor es básicamente el siguiente:

  1. Establece una conexión con el servidor y hace una petición a algún archivo de la página.
  2. El servidor analiza la petición del cliente, el servidor responde.
  3. Si la petición es correcta les devuelve el archivo que se pidió, si es incorrecta devuelve un mensaje de error.
  4. El navegador recibe el archivo que solicitó y construye la información para mostrar el contenido de la página en modo gráfico.

En esta parte del texto vamos a ver como se realizan éstas peticiones.

Formato de una petición

Un mensaje de petición de un cliente a un servidor incluye, en la primer línea del mensaje, el método que se le va a aplicar al archivo objetivo, la dirección del archivo objetivo y la versión del protcolo que se usará. A ésta linea del mensaje se le llama petición. Resumiendo todo lo que acabamos de ver, la línea de petición se organiza de la siguiente manera:

[MÉTODO] [/ruta­ del ­recurso] [Versión ­del ­protocolo]

El final de la petición se indica dando dos retornos de carro (\n\n).

Los métodos son explicados en el siguiente apartado. Después de la línea de petición siguen las cabeceras, un mensaje enviado al servidor en formato MIME­like que indica información del cliente. Al final de las cabeceras puede haber un mensaje extra, el cuerpo del mensaje: información que puede ser procesada por el servidor.

La ruta del recurso es la dirección del recurso (archivo) con el que se va a trabajar. Esta ruta puede ser especificada de forma absoluta, relativa o con la opción *.

Ruta­del­recurso = * | URIabsoluto | ruta_absoluta

Con el * se indica que el método no se aplica directamente a un recurso, sino al servidor entero. Un ejemplo podría ser:

OPTIONS * HTTP/1.1

Esta petición indica que el método OPTIONS (explicado más adelante) se va a aplicar a todo el servidor.

La forma "URIabsoluto" es requerida cuando la petición está siendo hecha a un proxy. Se le solicita al proxy que reenvíe la petición y regrese la respuesta. Un ejemplo de una petición usando "URIabsoluto" sería:

GET http://www.servidor.com/directorio/index.html HTTP/1.1
     (la ruta completa es especificada, incluyendo el nombre de servidor)

El URIabsoluto debería ser aceptado en todos los servidores HTTP, aunque los clientes HTTP/1.1 sólo la generen en peticiones a proxys. De otro modo se utiliza la ruta_absoluta.

La ruta_absoluta especifica sólamente la ruta del recurso, sin indicar el nombre del servidor. El nombre del servidor se especifica posteriormente en el campo "Host:" de las cabeceras (veremos cabeceras mas adelante).Un ejemplo de una petición usando abs_path podría ser esta:

GET /directorio/index.html HTTP/1.1
Host: www.servidor.com
   (el nombre del servidor es indicado en las cabeceras y no en la línea de petición)

Cabe aclarar que se debe de especificar una ruta obligatoriamente; si no hay una presenta en la URL se debe de dar "/" (raíz del servidor).

Entonces, si se le hace una petición a http://www.google.com.mx/index.html por medio de un proxy, se realizaría de la siguiente manera:

GET http://www.google.com.mx/index.html HTTP/1.1

Después al ser reenviada por el proxy, o al ser enviada desde un cliente directamente se haría:

GET /index.html HTTP/1.1
Host: www.google.com.mx

En resúmen, el URIabsoluto especifica el nombre del servidor en la línea de petición y es usado por proxys. La ruta_absoluta específica el nombre del servidor en el campo Host de las cabeceras y es usado por los clientes HTTP/1.1 normales. Si el host determinado por cualquiera de los métodos explicados no es válido en el servidor, el servidor responde con un mensaje de error "400 ­ Bad Request" (petición mala).

Métodos y cabeceras

El método indica la forma en la que se va a usar el recurso. El método es "case-sensitive" (diferencia mayúsculas entre minúsculas), por lo tanto no es lo mismo enviar "GET / HTTP/1.1" que "get / HTTP/1.1", este último ejemplo origniaría un mensaje de error del servidor.

Los siguientes son algunos de los métodos que pueden ser aceptados por un servidor: (existen más pero sólo explicaré los más esenciales)

  1. GET
  2. HEAD
  3. POST
  4. OPTIONS
  5. Otros métodos de interés que puedes buscar son PUT y DELETE

La lista de los métodos aceptados en un servidor pueden ser especificados en el archivo de configuración del servidor (debe haber un campo llamado "allow-method field" o "response-header" donde se especifican los métodos aceptados).

Cuando se realiza una petición, el servidor responde con un código de respuesta para notificar al cliente si el método que lleva la petición está permitido en el recurso o no, ya que la lista de métodos permitidos puede variar de un recurso a otro. Los servidores deberían responder con el código "405 ­ Method not Allowed" (método no permitido) si el método es conocido por el servidor pero no se permite en el recurso señalado, y con el código "501 ­ Not Implemented" (no implementado) si el método no es reconocido o implementado en todo el servidor. Más adelante hay una referencia a los mensajes del servidor con sus significados.

Los métodos GET y HEAD deben de ser soportados en todos los servidores de propósito general. Todos los demás métodos son opcionales de acuerdo a las necesidades del servidor; de cualquier manera, si los métodos son aceptados deben de ser usados únicamente con la función para la que se hicieron. Las funciones que veremos a continuación.

GET

El método GET se usa para obtener la información (sea cual sea) contenida en el recurso; en el caso de un index.html se devuelve el código fuente de la página. Si el recurso refiere a un proceso que produce información, entonces la información producida es la que recibe el ciente en lugar del código entero (como un script PHP). La respuesta de un método GET puede almacenarze en el caché (memoria temporal) del navegador si se especifica en las cabeceras, para que la próxima vez que se haga la misma petición la información se cargue más rápido.

Ejemplo de una petición GET hecha desde un navegador a www.google.com.mx:

GET / HTTP/1.1 Host: www.google.com.mx
User­Agent: Mozilla/5.0 (X11; U; Linux i686; es-AR) Debian/etchsecurity Firefox/2.0.0.12
Accept: text/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept­Language: es­ar,es;q=0.8,en­us;q=0.5,en;q=0.3
Accept­Charset: ISO­8859­1,utf­8;q=0.7,*;q=0.7
Connection: keep-­alive

Analizemos el mensaje HTTP:

  1. GET / HTTP/1.1: La primera línea específica el método (GET), el recurso (/) y el protocolo (HTTP/1.1).
  2. La segunda línea especifica el host al que se conecta por el campo Host:.
  3. La tercer línea es el campo User­Agent, este campo especifica el cliente que se está usando (Firefox 2.0 sobre Linux).
  4. Accept es el campo donde se especifica que tipo de contenido acepta el cliente, (texto, xml, html, imágenes, flash, etc...).
  5. Accept­Language dice que tipo de sistema de lenguaje usa el cliente, en este caso español e inglés.
  6. Accept­Charset contiene los tipos de codificación que acepta el navegador (iso, utf)
  7. Connection: keep­alive le indica al servidor que la conexión se debe de mantener abierta.

Todas estas líneas que vienen después de la línea de petición son las cabeceras de las que hemos hablado, las cabeceras como ya dijimos contienen información del cliente. Y el envío de estas puede variar, dependiendo de que requiera el servidor. Mas tarde analizaremos las cabeceras mas populares.

Respuesta del servidor:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html
Set-Cookie: PREF=ID=0c567c6cdb4c008f:TM=1165816162:LM=1165816162; expires=Sun, 17­Jan­2038 19:14:07 GMT; path=/; domain=.google.com.mx
Server: GWS/2.1
Date: Tue, 01 Jul 2008 03:52:22 GMT
 
<html>......
  1. La primera línea indica la versión del protocolo y el mensaje del servidor, en este caso un mensaje "200­ - OK".
  2. La segunda línea indica el control del caché que se va a dar.
  3. Content-Type indica el tipo de contenido que se envía, en este caso texto en html.
  4. Con set-cookie el documento envía una cookie al cliente.
  5. Después se da información del tipo de servidor. Y por último la fecha y hora.

Seguido de 2 retornos de carro comienza el cuerpo del mensaje. El cuerpo es lo que el cliente solicito con el método GET. En este caso Google envía el código fuente de la página.

HEAD

El método HEAD es idéntico a GET, excepto que el servidor no debe de regresar un cuerpo de mensaje en la respuesta. El contenido de información de cabeceras es el mismo que en una petición GET. De modo que si hacemos una petición a www.google.com.mx como la anterior cambiando GET a HEAD, obtendríamos la misma respuesta pero sin que el servidor nos de el cuerpo del documento. En concreto, solo obtenemos las cabeceras del servidor.

Podemos utilizar a HEAD como herramienta de enumeración de aplicaciones; es decir, conseguir la versión del servidor y, en algunos casos, la versión del sistema operativo en el que corre.

Veamos un ejemplo de usar HEAD como enumerador de servicios realizando la siguiente petición:

HEAD / HTTP/1.1
Host: www.linux.com
 
Respuesta:
HTTP/1.1 200 OK
Date: Tue, 01 Jul 2008 03:59:39 GMT
Server: Apache/1.3.33 (Unix) mod_gzip/1.3.26.1a mod_perl/1.29
SLASH_LOG_DATA: shtml
X-Powered-By: Slash 2.005000
X-Bender: Crippling pain? That's not covered by my insurance fraud.
Vary: User-Agent,Accept-Encoding
Connection: close
Content-Type: text/html; charset=iso­8859­1

Realizamos un "HEAD" a www.linux.com. El cual nos responde: "Server: Apache/1.3.33 (Unix) mod_gzip/1.3.26.1a mod_perl/1.29", esto nos proporciona información sobre el servidor.

POST

El método POST es usado para enviar información desde el cliente al recurso. Si lo decimos de otra forma, POST envía datos a un recurso, el recurso los recibe y hace lo que tenga que hacer con ellos.

Los usos para POST pueden ser varios, un ejemplo sería enviar un mensaje desde el cliente al recurso para poner un mensaje en un tagboard o foro, enviar los resultados de una encuesta, o registrar un usuario en un sitio. Es el método que comúnmente se utiliza cuando envías un formulario.

Hasta ahora hemos visto que un mensaje HTTP se compone de una línea de petición y cabeceras. En ejemplos anteriores vimos que, por ejemplo, en una petición HEAD se pueden omitir casi todas las cabeceras. Sin embargo, una petición POST requiere de varias cabeceras específicas para que pueda ser aceptada. Analizemos la siguiente petición POST de un cliente a un tagboard:

POST /tagboard/mensajes.php HTTP/1.1
Host: www.host.com
Accept: text/html, text/plain, image/gif, image/x-xbitmap, image/jpeg, application/x-shockwave­-flash, */*
Referer: www.host.com
Accept-Language: es-­ar
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Solaris; U; SunOS; es-ar; rv:1.7.8) Gecko/20060921 Firefox/1.5
Connection: keep-­alive
Content-Lenght: 77
Cookie: Hash=d41d8cd98f00b204e9800998ecf8427e;
 
nick=trew&url=http://trew.icenetx.net&mensaje=Hola%20que%20tal&Submit=Enviar
  1. La primera línea hace una petición POST a /tagboard/mensajes.php, indicando que el cliente le va a enviar datos a este recurso. Después se indica el Host.
  2. Referer, esta es interesante, le indica al servidor la dirección de donde viene el navegador, esta cabecera es requerida en una petición POST. En este caso Referer indica desde donde han sido enviados los datos, ya que es la página de donde viene el navegador.
  3. Content-Type indica el tipo de información que se envía, esta también es requerida para una petición POST, en este caso el valor es de "application/x­www­form­urlencoded", lo cual indica que la información son datos en formato "URL encode" enviados desde un formulario.
  4. Content-Lenght es otra cabecera requerida para POST, aquí se especifica la cantidad exacta de bytes de la cadena de datos que se envía al recurso.
  5. Y al final tenemos Cookie, en este caso es un hash. No explicaré que es una cookie, pero hay mucha información en la red.

Luego contamos con 2 retornos de carro y se envía el cuerpo del mensaje (también llamado "cuerpo de datos).

En este caso se envían los valores de las variables nick, url, mensaje y submit, que son los datos que se requieren para el tagboard.

El contenido del cuerpo del mensaje, como ya se especificó en la cabecera Content­Type, está en formato URL encode (codificado en URL). El cuerpo de datos siempre se encuentra dos retornos de carro después de las cabeceras. En peticiones en las que se envía información a un recurso por medio de un cuerpo de datos (como en POST), el servidor esperara a que se ingrese la información dos retornos de carro después de las cabeceras.

Las respuetas al método POST no se pueden almacenar en el caché.

OPTIONS

El método OPTIONS pide al servidor información sobre los métodos de petición/respuesta permitidos. Se usa para que el cliente pueda determinar que negociaciones puede realizar con el servidor. OPTIONS es el único método donde se puede usar un "*" como recurso. Cuando se aplica el "*" la información obtenida es del servidor como un todo, especificando los métodos permitidos. Si se indica otro recurso la información es solo de ese recurso. Ejemplo de un OPTIONS:

OPTIONS * HTTP/1.1
Host: www.linux.com
 
Respuesta:
HTTP/1.1 200 OK
Date: Tue, 01 Jul 2008 11:31:57 GMT
Server: Apache/1.3.33 (Unix) mod_gzip/1.3.26.1a mod_perl/1.29
X-Powered-By: Slash 2.005000
X-Fry: That's a chick show. I prefer programs of the genre: World's Blankiest Blank.
Content-Length: 0
Allow: GET, HEAD, OPTIONS, TRACE
Connection: close

Con OPTIONS * HTTP/1.1 el servidor nos regresa el "allow-method field" (hablamos anteriormente de él), el cual nos indica que métodos están permitidos en el servidor.

NOTA: El servidor nos ha cerrado la conexión después de la petición porque no se especifico "Connection: keep­alive" en las cabeceras del mensaje.

Respuestas del Servidor

Cuando un servidor recibe una petición, inmediatamente lo que hace es validarla para poder dar una respuesta al cliente. En el siguiente enlace se enlistan las posibles respuestas que puede dar un servidor con sus significados:

 http://teaching.cs.uml.edu/~heines/tools/HTTPServerResponseCodes.htm

Desarrollo de aplicaciones HTTP

Al momento de desarrollar una aplicación HTTP, lo que se haces es un programa que sea capaz de comunicarse con un servidor web y mandarle mensajes. En este apartado obviamente no voy a enseñar programación, simplemente voy a explicar como debería de estar diseñada una aplicación HTTP.

Ya que nos estamos comunicando con una aplicación HTTP, pues tendremos que basarla en el protocolo HTTP. Ok, puede que esto suene un poco bobo, pero me refiero a que debemos de hacer que el programa envíe las peticiones al servidor de la manera adecuada.

Antes de desarrollar nuestra primera aplicación, vamos a hacer manualmente lo que haría la aplicación. El objetivo es que realices una petición HTTP fuera de tu navegador; enviar tu propio mensaje, y recibir una respuesta del servidor. Ver más allá de tu navegador.

Telnet es una aplicación que viene integrada tanto en Windows como en Linux. Es una aplicación que sirve para acceder mediante una red o internet a otra máquina, y poder comunicarnos con ella. Los navegadores web están basados en telnet.

Utilizaremos telnet para este ejemplo.

  1. Para abrir telnet en windows nos vamos al menú Inicio, y seleccionamos ejecutar. En seguida entramos telnet y damos aceptar.
  2. Para abrirlo en Linux (¿hará falta explicarlo a un usuario de linux?), abrimos la consola y entramos el comando telnet.

A continuación vemos a telnet en espera de la entrada del usuario.

Le decimos a telnet que vamos a abrir una conexión a "yahoo.com", por el puerto 80 (ya que es el puerto a donde nos conectamos para interactuar con el servidor web)

telnet> open www.yahoo.com 80
Trying 209.191.93.52...
Connected to www.yahoo­ht2.akadns.net.
Escape character is '^]'.

Y vemos como telnet se conecta al servidor... Ahora que estamos conectados podemos enviarle mensajes al servidor. Así que les enviaremos un "GET / HTTP/1.1". Recordemos que debemos especificar el host. Si no incluimos la cabecera "Connection: keep-alive", la conexión será cerrada inmediatamente después de recibir la respuesta del servidor, entonces tendremos que volver a conectarnos al servidor para mandar otra petición (talvez ahora entiendas porque decíamos que HTTP/1.1 fue diseñado para mantener conexiones más estables).

NOTA: Si estás usando Windows puede que lo que escribas no se vea en tu pantalla, sin embargo podrás ver la respuesta del servidor.

Mándamos la petición:

GET / HTTP/1.1
Host: www.yahoo.com

Recuerda que cuando des los 2 retornos de carro finales, el servidor sabe que la petición ha sido completada, entonces la petición es enviada. Mientras no demos el retorno de carro final la petición nunca se enviará.

Si realizamos la petición correctamente, deberíamos de obtener una respuesta del servidor:

HTTP/1.1 200 OK
Date: Tue, 01 Dec 2008 12:13:19 GMT
P3P: policyref="http://p3p.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR CNT STA POL HEA PRE GOV"
Cache-Control: private
Vary: User-Agent
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf­8 2746
 
<html><head>....Contenido

Muy bien, hemos realizado una petición HTTP manualmente. Es indispensable seguir las reglas de HTTP para poder tener una respuesta satisfactoria, de lo contrario no obtendremos lo que queremos. Lo que acabamos de hacer es lo que ocurre todo el tiempo con los navegadores.

Cuando ingresamos una URL, el navegador se conecta al Host por medio de un socket, con una conexión muy similar a la que hicimos con telnet. Después envía la petición al servidor, y recibe una respuesta junto con el HTML. Después arma el HTML para darle estructura gráfica y la muestra al usuario en forma de hyper-text.

Desde este punto de vista, desarrollar la función básica de una aplicación HTTP se ve relativamente simple. Para poder desarrollar una aplicación HTTP obviamente lo primero que se necesita es conocer y programar en algún lenguaje. Como necesitamos establecer conexiones con máquinas remotas, es necesario saber manejar sockets en el lenguaje en que programamos (la mayoría de los lenguajes de programación tienen librerías que dan soporte a sockets). Una vez que nos conectamos al servidor HTTP por el puerto TCP 80 con nuestro socket, podemos enviarle desde el programa la petición como si fuera texto plano; si la petición se envió de manera correcta entonces el servidor debería de emitir una respuesta para que el programa la reciba.

Lo único que hay que saber es programar con sockets y conocer HTTP para poder enviar peticiones, contando con estos conocimientos el desarrollo elemental de una aplicación HTTP es muy sencillo. No me puedo poner a enseñar programación ni sockets aquí, esto queda fuera de los objetivos del texto (además de que el contenido sería mucho más extenso de lo que ya es). Pero ahora sabes como funcionan las aplicaciones HTTP.

Todos los web exploits son aplicaciones HTTP que envían peticiones malignas a aplicaciones vulnerables. Si vas a algún sitio como http://milw0rm.com se pueden encontrar un montón de exploits de aplicaciones web que utilizan HTTP para mandar peticiones (casi todos están escritos en perl, PHP o python).

Interceptando Cabeceras

Ahora veremos de que formas se puede explotar una aplicación web interceptando y modificando las cabeceras que nuestro navegador envía a un servidor. Vamos a trabajar con software "Man-in-the-Middle", algunas vulnerabilidades y exploits.

Man-in-the-Middle

Un ataque conocido como Man-in-the-Middle, es cuando un atacante puede ver, insertar y modificar los mensajes enviados entre dos puntos. Hablando de HTTP, un programa MitM intercepta las cabeceras enviadas del cliente al servidor (o al revés), luego pueden ser modificadas y al final se envían. Viéndolo de una forma mas abstracta, un programa MitM , es un proxy; un medio que filtra los datos que pasan entre dos elementos.

  ___________________                                                        _______________________
 |\                  \                                                      /                      /|
 | \__________________\                  _____________                     /______________________/ |
 | |                  |.................|   Man in   |....................|                       | |
 | |  Cliente HTTP    | Cabeceras HTTP  | the Middle | Cab. Modificadas   |   Servidor HTTP       | |
 \ | ºººººººººººººº   |.................|____________|....................|  ººººººººººººººº      | /
  \|__________________|                                                   |_______________________|/

De esta forma podemos actuar como intermediario, entre una comunicación de cliente­servidor, para modificar todo lo que pase entre nosotros y lograr nuestros propios fines. Podrás encontrar una gran variedad de programas Man-in-the-Middle en internet.

En este texto trabjaré con un programa llamado Achilles, para hacer el texto más corto y sencillo. Sin embargo, yo recomiendo usar otros programas que incluyen más opciones tales como Burp Suit y WebScarab. Pueden descargar achilles de la sección de descargas de la web de ICEnetX: http://www.icenetx.net/?contenido=descargas.

Este no es un manual de Achilles así que me iré rápido con las opciones necesarias. En la parte de Proxy Settings:

fig 2
fig 2
  • En el campo Listen on Port ponemos 5000.
  • Cert file lo dejamos como está
  • Client y Server timeout lo ponemos en 5.

En Intercept mode:

  • Activamos Intercept Mode ON
  • Activamos Intercept Client data
  • Activamos Ignore .jpg/gif

Luego configuramos el navegador. Si usas Firefox ve a Preferencias-> Avanzado-> Network-> Configuración, seleccionamos Manual proxy configuration y en donde dice HTTP proxy ponemos 127.0.0.1 y en puerto 5000.

Cuando ya esté todo listo damos clic en el botón de Play del Achilles, y en cuanto nos intentemos conectar a alguna página con el navegador, si vamos a la ventana de Achilles podremos ver como aparecen todas las cabeceras que envió el navegador, ¡de lo que nunca nos dábamos cuenta antes!. Una vez en el Achilles podemoa analizar y modificar las cabeceras, para después mandarlas. (Mientras no oprimamos el botón Send las cabeceras no se enviarán y jamás le llegaran al servidor.

En la figura 2 podemos ver una captura de Achilles interceptando las cabeceras de una petición a http://trucos.todo-linux.com. Veamos que en vez de la cabecera Connection: keepalive se utilizó ProxyConnection, debido a que se utilizó un Proxy para establecer la conexión.

fig 3
fig 3

Si accedimos a todo-linux por medio de un enlace entonces en la petición aparece la cabecera Referer. Si una vez que estamos en la página buscamos en el panel izquierdo del sitio vamos a encontrar una sección llamada Nos enlazan desde: que contiene el URL de los sitios que enlazan a todo-linux. Como ya seguramente lo suposiste el contenido de esa sección lo obtienen de la cabecera Referer de los navegadores. Si interceptamos las cabeceras de la petición y cambiamos el valor de Referer por cualquier otra cosa no será sorpresa encontrar lo que sea que hayamos insertado en la sección de Nos enlazan. (fig 3).

Atacando por Cabeceras

Aquí es donde está el peligro: muchas aplicaciones utilizan las cabeceras para poder obtener inforamción del cliente (nombre del navegador, dirección IP, etc...). Pero no suelen tomar en cuenta que estas cabeceras pueden ser manipuladas por el usuario, así que no validan el contenido de las cabeceras.

Tomemos un ejemplo de ataque. La cabecera X_FORWARDED_FOR (cabecera que envían los proxys donde se almacena la verdadera IP del cliente) es muy utilizada por varios CMS y aplicaciones para obtener la dirección IP de un cliente. La vulnerabilidad se puede dar al momento en el que una aplicación obtenga la IP del cliente por medio de X_FORWARDED_FOR y luego vea si hay una IP igual en la base de datos (para ver si el IP está baneado):

$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$consulta = mysql_query( "SELECT * FROM baneados where ip=$ip" , $conexion);

El valor de la variable $ip es el mismo que el de X_FORWARDED_FOR, y luego se realiza una sentencia SQL con la variable sin hacerle ningún tipo de validación. Así que si con un man-in-the-middle modificamos la cabecera por algo cómo foo'; DROP TALE baneados;-- cuando se realize la consulta la sentencia quedará así:

SELECT * FROM baneados where ip='foo'; DROP TABLE baneados;--'

Lo cual resulta en una inyección SQL y la tabla baneados queda eliminada.

Es sólo un ejemplo sencillo. Pero hay muchas aplicaciones que usan varias cabeceras (Cookie, User-Agent, etc...) para realizar consultas y operaciones con ellas. Como programador se debe tener cuidado y validar TODOS LOS MEDIOS DE ENTRADA. Todo lo que se envía del cliente al servidor puede ser interceptado y falsificado (spoofing). Además de ataques como SQL Injection y XSS también se pueden hacer ataques de Cross-user defacement y cache poisoning por medio de Response Splitting; otro ataque que se hace por cabeceras.

Conclusiones

Debido a que quería mantener la extensión del documento lo más corta posible, en algunas partes tuve que cubrir sólo básico. Aún así espero que si llegaste al final del texto ahora puedas entender la manera en la que funciona HTTP. Todavía hay muchas cosas por saber así que no te quedes corto e investiga más.

Cualquier duda o comentario me la pueden hacer llegar a mi correo: trew@icenetx.net .

Pueden distribuir este texto siempre y cuando no se modifique su contenido original.

Por Rubén Ventura Piña (Trew), "Maybe you can't break the system, but you can always hack it"

Herramientas personales