Mi anagrama

La pesadilla de la codificación de las URI

¿Qué codificación hay que usar? Nota: es una página en desarrollo...

 


En esta página

Introducción

Experimentos

Resultados

Análisis

Referencias

Viene de

Detalles (así se hizo)


Índice general

Principal

Noticias

Direcciones

Detalles (así se hizo)

Asignaturas

Compiladores/Procesadores de Lenguajes

TALF/CyC

Programación en C

Metodología de la programación

Investigación

DELi

LinguaMedia

MoreLab (en DeustoTech)

Linked Data Spain

TIMM

Red Española de e-Ciencia

SEPLN

JENUI 2006

AENUI

EAPLS

EPI (Euskarazko Programazio Ingurunea)

Otros mundos

Fotos

Música

Otros mundos más lejanos

RDF Resource Description Framework Icon (for humans)

icono de twitter icono de flickr

Sección Sindical de CCOO de la Universidad de Deusto (UD Bilbao)

Campañas

Plataforma Víctimas ALVIA 04155

stopsoftwarepatents.eu petition banner

Plataforma cidadá Nunca Máis

 

[2005-03-14]

Introducción

El propósito de este estudio es analizar el comportamiento de los códigos de escape de las URI [RFC2396]. Como se sabe, consiste en usar el carácter "%", junto con el código hexadecimal del carácter que se quiera incluir en la URI. Así por ejemplo, "%26" representa el carácter "&", "%3A" representa el carácter ":", etc.

Experimentos

Se han usado principalmente dos direcciones web, una que hace una petición a un servidor Zope sobre Apache, y otra a Google. La primera contiene los caracteres "?", "=", "&" y ":", mientras que la segunda solamente usa "?", "=" y "&". Cada dirección se acompaña debajo de su texto literal (parece ser que los navegadores interpretan las direcciones cuando las muestran en la barra de estado al pasar el ratón por encima del enlace), que se muestra troceado para facilitar su lectura.

Experimento 1: URI codificada con %26 ("&") y %3A (":")

Primera dirección:

Zope

http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK?
  val=%26vis=%26Dep=%26cor%3Alist=C-2003%26cor%3Alist=D-UD%26
  sort_index=bobobase_modification_time

Segunda dirección (no usa %3A):

Busca en Google

http://www.google.com/search?num=20%26hl=en%26lr=%26safe=off%26
  c2coff=1%26q=ampersand+escape+HTML%26btnG=Search

Experimento 2: URI codificada con & ("&") y %3A (":")

Primera dirección:

Zope

http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK?
  val=&vis=&Dep=&cor%3Alist=C-2003&cor%3Alist=D-UD&
  sort_index=bobobase_modification_time

Segunda dirección (no usa %3A):

Busca en Google

http://www.google.com/search?num=20&hl=en&lr=&safe=off&
  c2coff=1&q=ampersand+escape+HTML&btnG=Search

Experimento 3: URI codificada con & ("&"), %3F ("?"), %3D ("=") y %3A (":")

Primera dirección:

Zope

http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK%3F
  val%3D&vis%3D&Dep%3D&cor%3Alist%3DC-2003&cor%3Alist%3DD-UD&
  sort_index%3Dbobobase_modification_time

Segunda dirección (no usa %3A):

Busca en Google

http://www.google.com/search%3Fnum%3D20&hl%3Den&lr%3D&safe%3Doff&
  c2coff%3D1&q%3Dampersand+escape+HTML&btnG%3DSearch

Experimento 4: URI codificada con & ("&"), %3D ("=") y %3A (":")

Primera dirección:

Zope

http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK?
  val%3D&vis%3D&Dep%3D&cor%3Alist%3DC-2003&cor%3Alist%3DD-UD&
  sort_index%3Dbobobase_modification_time

Segunda dirección (no usa %3A):

Busca en Google

http://www.google.com/search?num%3D20&hl%3Den&lr%3D&safe%3Doff&
  c2coff%3D1&q%3Dampersand+escape+HTML&btnG%3DSearch

Resultados

La primera impresión es que el primer experimento (que usa %26) no funciona (ni en Zope ni en Google), mientras que los otros dos (que usan &) parecen producir los mismos, por ende correctos, resultados. Desde luego, en el caso de Google, no es posible distinguir entre el segundo resultado y el tercero en cuanto a la salida obtenida.

Resultados en Zope

Sin embargo, la llamada a Zope se ha programado especialmente para que se muestren las variables de la petición que se recibe y que construye internamente el servidor. En particular, la salida recoge las variables FORM y QUERY_STRING, cuyo valor se ha copiado sinópticamente a continuación.

Primer experimento (con %26 y %3A):

FORM:         {'val': '&vis=&Dep=&cor:list … '}
QUERY-STRING: val=%26vis=%26Dep=%26cor%3Alist …

Segundo experimento (con & y %3A):

FORM:         {'cor': ['C-2003', 'D-UD'], 'Dep': '', … }
QUERY-STRING: val=&vis=&Dep=&cor%3Alist=C-2003 …

Tercer experimento (con &, %3F, %3D y %3A):

FORM:         {'cor': ['C-2003', 'D-UD'], 'Dep': '', … }
QUERY-STRING: val=&vis=&Dep=&cor:list=C-2003 …

Hago notar que la variable FORM obtiene exactamente el mismo valor en el segundo y en el tercer experimento, pero lo sorprendente es que la variable QUERY_STRING difiere entre uno y otro. Obsérvese cómo en el segundo experimento aparece el código %3A, mientras que no está en el tercer experimento.

Registros de Apache y Zope

Para completar el estudio, se han recogido las líneas apuntadas por los servidores Apache y Zope en sus correspondientes registros (access.log y Z2.log, respectivamente). Se muestran troceadas para facilitar su lectura (de todos modos, se puede acceder al texto original). Es importante indicar que de cada petición se han generado dos líneas en cada registro (una para la petición propiamente dicha y otra para la imagen que se observa en la salida). Esta última es una línea muy significativa, como se verá después.

El formato de ambos registros es similar. En primer lugar, se recoge la fecha, después la petición (que empieza con GET y termina en HTTP/1.1), luego dos códigos de resultado (me imagino), y finalmente la página que hace la petición (es importante prestar atención a ella, justamente en la segunda línea que se registra). Se omiten otros datos del registro (como la IP o el USER_AGENT), ya que se entiende que no están afectando al problema.

Primer experimento (con %26 y %3A):

access.log [apache]

[14/Mar/2005:12:05:12 +0100]
GET /AboutUs/Members/JosuKa/pruamp_JK?
    val=%26vis=%26Dep=%26cor%3Alist=C-2003%26cor%3Alist=D-UD%26
    sort_index=bobobase_modification_time HTTP/1.1
200 7202
http://paginaspersonales.deusto.es/josuka/jkamp.asp

[14/Mar/2005:12:05:13 +0100]
GET /p_/ZopeButton HTTP/1.1
304 0
http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK?
    val=%26vis=%26Dep=%26cor%3Alist=C-2003%26cor%3Alist=D-UD%26
    sort_index=bobobase_modification_time

Z2.log [zope]

[14/Mar/2005:12:05:12 +0200]
GET /VHosts/Deli/AboutUs/Members/JosuKa/pruamp_JK?
    val=&vis=&Dep=&cor:list=C-2003&cor:list=D-UD&
    sort_index=bobobase_modification_time HTTP/1.1
200 7387
http://paginaspersonales.deusto.es/josuka/jkamp.asp

[14/Mar/2005:12:05:13 +0200]
GET /VHosts/Deli/p_/ZopeButton HTTP/1.1
304 273
http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK?
    val=%26vis=%26Dep=%26cor%3Alist=C-2003%26cor%3Alist=D-UD%26
    sort_index=bobobase_modification_time

Segundo experimento (con & y %3A):

access.log [apache]

[14/Mar/2005:12:05:15 +0100]
GET /AboutUs/Members/JosuKa/pruamp_JK?
    val=&vis=&Dep=&cor%3Alist=C-2003&cor%3Alist=D-UD&
    sort_index=bobobase_modification_time HTTP/1.1
200 7380
http://paginaspersonales.deusto.es/josuka/jkamp.asp

[14/Mar/2005:12:05:16 +0100]
GET /p_/ZopeButton HTTP/1.1
304 0
http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK?
    val=&vis=&Dep=&cor%3Alist=C-2003&cor%3Alist=D-UD&
    sort_index=bobobase_modification_time

Z2.log [zope]

[14/Mar/2005:12:05:15 +0200]
GET /VHosts/Deli/AboutUs/Members/JosuKa/pruamp_JK?
    val=&vis=&Dep=&cor:list=C-2003&cor:list=D-UD&
    sort_index=bobobase_modification_time HTTP/1.1
200 7565
http://paginaspersonales.deusto.es/josuka/jkamp.asp

[14/Mar/2005:12:05:16 +0200]
GET /VHosts/Deli/p_/ZopeButton HTTP/1.1
304 273
http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK?
    val=&vis=&Dep=&cor%3Alist=C-2003&cor%3Alist=D-UD&
    sort_index=bobobase_modification_time

Tercer experimento (con &, %3F, %3D y %3A):

access.log [apache]

[14/Mar/2005:12:05:19 +0100]
GET /AboutUs/Members/JosuKa/pruamp_JK%3F
    val%3D&vis%3D&Dep%3D&cor%3Alist%3DC-2003&cor%3Alist%3DD-UD&
    sort_index%3Dbobobase_modification_time HTTP/1.1
200 7372
http://paginaspersonales.deusto.es/josuka/jkamp.asp

[14/Mar/2005:12:05:19 +0100]
GET /p_/ZopeButton HTTP/1.1
304 0
http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK%3F
    val%3D&vis%3D&Dep%3D&cor%3Alist%3DC-2003&cor%3Alist%3DD-UD&
    sort_index%3Dbobobase_modification_time

Z2.log [zope]

[14/Mar/2005:12:05:19 +0200]
GET /VHosts/Deli/AboutUs/Members/JosuKa/pruamp_JK?
    val=&vis=&Dep=&cor:list=C-2003&cor:list=D-UD&
    sort_index=bobobase_modification_time HTTP/1.1
200 7557
http://paginaspersonales.deusto.es/josuka/jkamp.asp

[14/Mar/2005:12:05:19 +0200]
GET /VHosts/Deli/p_/ZopeButton HTTP/1.1
304 273
http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_JK%3F
    val%3D&vis%3D&Dep%3D&cor%3Alist%3DC-2003&cor%3Alist%3DD-UD&
    sort_index%3Dbobobase_modification_time

Observando los anteriores registros, se concluye claramente que Apache no practica ninguna clase de interpretación de los códigos de las URI, si es que anota en el registro exactamente lo que hace, que debería ser lo lógico. Justamente esto es lo que sorprende (por la razón contraria) del otro sevidor, Zope.

En efecto, al ver la primera línea de cada registro de Zope (donde después de GET aparece la URI que se ha solicitado), uno puede pensar que Zope está interpretando todos los códigos de URI. Sin embargo, no debe ser así, porque si miramos la segunda línea (la que solicita ZopeButton), vemos que el peticionario (la URI precedente, por lo tanto), aparece con los códigos URI originales.

La duda que nos asalta entonces es si Zope está solamente anotando la URI interpretada (pero resolviendo con la original), o está anotando la URI interpretada porque es la que usa para resolver el recurso solicitado. Aquí debemos echar un vistazo a los resultados mostrados por el script, tal como se han mostrado en [1], para concluir que Zope a veces interpreta la URI y a veces no. Vemos en efecto que la salida (variable QUERY_STRING) del primer experimento contiene los códigos de escape URI originales, que la del segundo también (el único, que era el %3A), pero la del tercero no. Y ahí es donde me han matao.

Análisis

La prueba del nueve. El único carácter que parece tener sentido en una URI y a la vez puede ser el identificador de un recurso es el sostenido (#). Se ha creado un recurso (una imagen) en el servidor Zope de nombre jk#lespesor.png. Es curioso, porque cuando intento usar ?, & o : para el nombre, Zope me dice que no son caracteres válidos en una URI, pero # sí lo admite (vale, ya lo he entendido: esa parte no va en la petición que le llega al servidor, se la queda el cliente y la usa una vez que ha obtenido la respuesta). Véase cómo la siguiente dirección, cuyo código literal se muestra debajo:

dirección incorrecta de la imagen

http://www.deli.deusto.es/AboutUs/Members/JosuKa/jk#lespesor.png

no funciona (como es lógico, ya que se entiende que #lespesor.png es el modificador a un recurso llamado jk), mientras que:

dirección correcta de la imagen

http://www.deli.deusto.es/AboutUs/Members/JosuKa/jk%23lespesor.png

funciona perfectamente, ya que una vez recibida la petición, la cadena jk%23lespesor.png se decodifica en jk#lespesor.png), que sí es el nombre del recurso.

Así que imaginemos que quiero pasar un nombre de recurso a mi script, para que este lo procese, y lo muestre en la salida. Veamos las siguientes peticiones, que usan primero un nombre aséptico (jklespesor.png), es decir, sin caracteres problemáticos para las URI:

muestra imagen con nombre aséptico

http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_rec_JK?imagen=jklespesor.png

Ahora le quiero pasar el nombre jk#lespesor.png. Veamos que la petición:

no muestra imagen con nombre no aséptico

http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_rec_JK?imagen=jk#lespesor.png

no funciona, porque el carácter # se interpreta como lo que significa en una URI, así que ni siquiera viene recogido en la variable QUERY_STRING. Así que tengo que codificarlo con el código %23:

sí muestra imagen con nombre no aséptico

http://www.deli.deusto.es/AboutUs/Members/JosuKa/pruamp_rec_JK?imagen=jk%23lespesor.png

Por supuesto, es interesante darse cuenta que el programador del script ha de recoger el nombre que finalmente le llega a través de la variable imagen (en este caso la cadena literal jk#lespesor.png), y aplicarle la codificación como URI en que se va a convertir. Por eso, en la salida de la petición anterior, la primera línea no muestra ninguna imagen (el nombre no se ha colocado con los códigos de escape pertinentes), mientras que la segunda se ve correctamente (ver el código fuente de las páginas de resultado para apreciar las diferencias en el atributo src de la imagen).

imagen al principio

imagen al principio

imagen al principio

imagen al principio

imagen al final

imagen al final

imagen al final

imagen al final

Referencias

HTML 4.01/6.4 URIs

RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax

RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1

Escape special characters

[ZPT] ampersand escape problem

URIUtil.Coder (HttpClient 2.0.2 API)

HTML output serializes ampersand as "&" in HREF attributes

[Medusa-dev] uri % resolving still broken (both release and CVS)

[Medusa-dev] Small patch to move URI unquoting in http_server.py

Bug in parsing request paths, and patch

Mi anagrama peque


Mi anagrama peque

Copyright © 2005 JosuKa Díaz Labrador

Facultad de Ingeniería, Universidad de Deusto, Bilbao, España

Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

Última modificación: 2005-03-14. Accesos al sitio: 560623

Valid XHTML 1.1! Valid CSS!