WebPy es un framework para aplicaciones web escrito en python. Se trata de un framework de código libre y está "licenciado" como dominio público.
En este caso queremos mejorar la seguridad de nuestra aplicación web creando una clase que implemente mecanismos de filtrado por lista negra. Existen múltiples listas negras en internet sobre IPs potencialmente dañinas.
En mi caso he decido para implementar esta funcionalidad con la lista negra suministrada por el proyecto honeypot.
En primer lugar vamos a crear una clase que nos permita hacer consultas sobre esta base de conocimiento.
class HttpBL(): def __init__(self, key): self.key = key self._DOMAIN = "dnsbl.httpbl.org" def get_info(self, ip, timeout=None): partes = ip.split(".") #Don't ask private networks if partes[0] == "10" or partes[0] == "127": return (-1,-1,-1) elif partes[0] == "192" and partes[1] == "168": return (-1,-1,-1) elif partes[0] == "172" and int(partes[1]) >= 16 and int(partes[1]) <= 31: return (-1,-1,-1) domain_ask = "" for parte in partes: domain_ask = parte+"."+domain_ask domain_ask = self.key+"."+domain_ask+self._DOMAIN try: if timeout: socket.timeout = timeout result = socket.gethostbyname(domain_ask) result = result.split(".") return (int(result[1]), int(result[2]), int(result[3])) except socket.gaierror as (error, string): if error == 11001 or error == -5: return (-1,-1,-1) else: raise socket.gaierror(error, string)Como podeis observar, las IPs privadas no se consultan.
Podeis obtener más información sobre el tipo de información que nos provee esta lista desde: http://www.projecthoneypot.org/httpbl_api.php
Por otro lado he implementado una clase que, además de comprobar que quien acceda al servicio no sea potencialmente peligroso, define: unas reglas de acceso directamente en la declaración, la creación de una respuesta OPTIONS con los datos anteriores y una comprovación del dominio desde el que se está acediendo a la aplicación.
class SecureWeb(): def __init__(self, blkey="", host_name=None, allow_methods=["HEAD","GET","POST","OPTIONS"]): self.allow_methods = allow_methods self.host_name = host_name self.blacklist = HttpBL(blkey) def not_allowed(self): raise web.Forbidden() def wrong_hostname(self): self.not_allowed() def bad_method(self): self.not_allowed() def to_honeypot(self, data): self.not_allowed() def to_search_engine(self): pass
def check_allow(self):
(last_activity, threat_score, type) = self.backlist.get_info(web.ctx.env["REMOTE_ADDR"]) if type == 0: self.to_search_engine() elif type > 0: self.to_honeypot((last_activity, threat_score, type))
if self.host_name: hostname_ok = False for hostname in self.host_name: if hostname.lower() == web.ctx.env["SERVER_NAME"].lower(): hostname_ok = True break if not hostname_ok: self.wrong_hostname() if not web.ctx.env["REQUEST_METHOD"].upper() in self.allow_methods: self.bad_method() def HEAD(self, *extras): self.check_allow() def GET(self, *extras): self.check_allow() def POST(self, *extras): self.check_allow() def PUT(self, *extras): self.check_allow() def DELETE(self, *extras): self.check_allow() def TRACE(self, *extras): self.check_allow() def CONNECT(self, *extras): self.check_allow() def OPTIONS(self, *extras): self.check_allow() allow = "" for method in self.allow_methods: allow += method+"," allow = allow[:1] web.header('Allow', allow)
Como se puede observar en cada tipo de petición HTTP se realiza un chequeo de acceso, y en caso de no validarlo se envia la información a la función correspondiente para poder procesarla de forma adecuada. En el ejemplo todas las funciones acaban llamando a 'not_allowed' que muestra un Forbidden. Pero cada uno de vosotros podeis modificar las funciones 'wrong_hostname' 'bad_method' 'to_honeypot' 'to_search_engine' para que realicen las acciones que estimeis oportunas.
No hay comentarios:
Publicar un comentario