¿Qué codificación hay que usar? Nota: es una página en desarrollo...
[2005-03-14]
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.
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.
Primera dirección:
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):
http://www.google.com/search?num=20%26hl=en%26lr=%26safe=off%26 c2coff=1%26q=ampersand+escape+HTML%26btnG=Search
Primera dirección:
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):
http://www.google.com/search?num=20&hl=en&lr=&safe=off& c2coff=1&q=ampersand+escape+HTML&btnG=Search
Primera dirección:
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):
http://www.google.com/search%3Fnum%3D20&hl%3Den&lr%3D&safe%3Doff& c2coff%3D1&q%3Dampersand+escape+HTML&btnG%3DSearch
Primera dirección:
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):
http://www.google.com/search?num%3D20&hl%3Den&lr%3D&safe%3Doff& c2coff%3D1&q%3Dampersand+escape+HTML&btnG%3DSearch
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.
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.
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.
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).
RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax
RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
[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
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