Últimos Cambios |
||
Blog personal: El hilo del laberinto |
Última Actualización: 18 de octubre de 2005 - Martes
Hace dos años y medio, presenté un proyecto de autentificación Apache basado en mod_python.
El proyecto original tiene dos problemas:
Adicionalmente, los dos problemas anteriores conspiran para impedir que los diferentes "handlers" gemelos que puedan existir en varios dominios o directorios de un mismo dominio se puedan compartir, ocasionando una pesadilla de mantenimiento y seguridad.
Ambos problemas, una vez identificados, tienen en realidad una solución bastante sencilla.
El primer problema se soluciona haciendo que los datos de autorización "usuario", "contraseña" e intervalo temporal de validez, se lean desde un fichero externo, de texto puro (no un "script"). De esa forma el administrador del dominio puede realizar cambios sin la intervención del administrador de sistemas. El nuevo código del "handler" es el siguiente:
# Autentificacion controlada por horarios # y con acceso directorio a directorio from mod_python import apache # Autorizacion def authzhandler(request) : #return verifica_usuario(request) return apache.OK # Autentificacion def authenhandler(request) : return verifica_usuario(request) def verifica_usuario(request) : usuarios={} try : import os f=request.filename f=f[:f.rfind(os.sep)+1]+"claves.txt" f=open(f) for i in f : i=i.strip() if (not i) or i[0]=="#" : continue i=i.split(",") assert len(i[2])==12 assert len(i[3])==12 j=long(i[2]) # Comprobamos que sean numeros, aunque la comparacion k=long(i[3]) # posterior sera entre cadenas usuarios[i[0]]=(i[1],i[2],i[3]) except : request.content_type = "text/html" request.send_http_header() request.write("<h1>ERROR EN EL FICHERO DE CLAVES!!</h1><br><br><br>") return apache.HTTP_UNAUTHORIZED try : # Para que nos de el usuario, primero hay que pedir la clave clave=request.get_basic_auth_pw() usuario=request.connection.user if not usuarios.has_key(usuario) : return apache.HTTP_UNAUTHORIZED i=usuarios[usuario] if i[0]!=clave : return apache.HTTP_UNAUTHORIZED import time now=time.strftime("%Y%m%d%H%M") if (now<i[1]) or (now>i[2]) : return apache.HTTP_UNAUTHORIZED return apache.OK except : # Si ocurre algo raro, nos curamos en salud return apache.HTTP_UNAUTHORIZED
El fichero que contiene los parámetros de autentificación se llama "claves.txt", reside en el directorio a proteger, y tiene el formato siguiente:
# La sintaxis es la siguiente: # usuario,clave,tiempo de inicio,tiempo de final # los tiempos se especifican como YYYYMMDDHHMM. # Los comentarios empiezan con '#'. # La siguiente linea define un usuario "prueba", # con clave "prueba4" de validez todo el 2005 #prueba,prueba4,200501010000,200601010000 # El siguiente usuario solo tiene acceso desde # las 4 de la tarde del 26 de octubre a las 9 # de la noche de 2 de diciembre: # usuario,clave,200510261600,200512022100
Por supuesto, se pueden definir múltiples usuarios, cada uno con su propia clave e intervalo de validez. Basta con especificar cada uno de ellos en una línea.
En cuanto a la configuración Apache en sí, lo evidente es añadir lo siguiente:
<Directory el que sea> <Files "claves.txt"> Order Allow,Deny Deny from all </Files> Auth_MySQL off # Eliminamos la busqueda en el directorio actual PythonPath "sys.path[1:]+['path al directorio donde reside el "handler"']" ¡OJO!: Esto es incorrecto. ver párrafos siguientes PythonAuthenHandler autorizacion_horaria PythonAuthzHandler autorizacion_horaria AuthType Basic AuthName "Realm que queramos usar" #AuthUserFile "/dev/null" #AuthGroupFile "/dev/null" #AuthAuthoritative off require user </Directory>
Respecto a la versión anterior, se prohibe el acceso al fichero "claves.txt" a través del servidor web, por razones evidentes, y se modifica el "PythonPath" para, por un lado, eliminar la búsqueda en el directorio actual y, por otro, añadir la búsqueda en el directorio de referencia de "scripts" y "handlers", fuera de los ojos y los dedos de los usuarios.
La configuración anterior es técnicamente correcta, pero tiene un problema con la directiva "PythonPath". Dicha directiva se reevalúa en cada ejecución del "handler", por lo que nos iremos cargando el path de Python y acabará dejando de funcionar. O no, porque como la evaluación de "PythonPath" es bastante lenta, en realidad "mod_python" hace caché del resultado de una directiva "PythonPath" y reutiliza el resultado.
Pero las cosas se complican cuando se utilizan varios "handlers", ya que cada uno evalúa el "PythonPath" por separado, pero el resultado es compartido (lo que considero que es un bug de "mod_python", por cierto). Es decir, que si un "handler" altera el path de búsqueda de Python, este cambio afecta al resto de "handlers".
Para solucionar el problema, lo más seguro es utilizar un "PythonPath" global en la configuración del Apache, que afecte a todos los "handlers", o bien utilizar "PythonPath" privados pero estáticos y explícitos.
De hecho, si se emplea la configuración mostrada, tal cual, y tenemos varios "handlers" que la emplean, su funcionamiento será errático y esporádico, según el orden en que se vayan accesiendo las páginas y el proceso Apache concreto que enganchemos en cada petición. ¡NO LO HAGAS!. Como ya he dicho, entiendo que se trata de un bug en "mod_python", que podría estar solucionado ya cuando leas esto :-).
Más información sobre los OpenBadges
Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS