Translate

jueves, 25 de septiembre de 2014

Ancho de banda y HOTLINKS, no cojas lo que no es tuyo

Puede que te preguntes que son los HOTLINK, con toda seguridad lo habrás visto muchas veces porque es una práctica muy habitual. 

Si te gusta una foto de una web,  podrías obtener su link y hacer referencia en tu blog, esta acción aparentemente inocente no debería realizarse sin el permiso del administrador de la web origen. Seguramente no haya existido ninguna mala intención, solo has enlazado una imagen, al fin y al cabo no te has apropiado de nada. Esto sería lo que se llama un HOTLINK.

La otra cara de la moneda es cuando comprobamos el ancho de banda de la web origen. En mayor o menor medida, todos los proveedores de servicios de hosting incluyen el consumo de ancho de banda como un parámetro de los servicios que ofertan. Cuando proyectas un sitio web y su alojamiento, el ancho de banda es una de las opciones que debes contratar teniendo en cuenta a qué tipo de clientes se dirige y el tipo de actividad.

Si el contenido de la web origen es muy bueno o atractivo, seguramente otros usuarios pueden realizar un HOTLINK a imagenes que les interesen, esto provoca que las imágenes que se sirven desde nuestra página no consumen nuestro ancho de banda, sino el ancho de banda de la web origen. Y si obtenemos muchas visitas, esto provocará que agotemos el ancho de banda de la web origen sin que se haya producido ninguna visita a ésta.

Una buena solución para evitar que se enlacen imágenes desde otrás web a nuestra página y consumiendo muy pocos recursos de máquina,  es indicar estas instrucciones en el fichero de configuración del sitio web - para Apache 2 - :

# evitar hotlinking de imagenes
SetEnvIf Referer ejemplo\.es localreferer

    Order deny,allow
    Deny from all
    Allow from env=localreferer
    Header set Cache-Control max-age=3600




Las imágenes pueden descargarse igualmente, pero evitaremos que se realicen HOTLINKS, en este caso el sitio web protegido es  EJEMPLO.ES . Pero en ocasiones, hemos contratado más de un nombre de dominio para el mismo sitio web y esto hace que cuando accedemos con el nombre EJEMPLO.COM, podemos ver la web pero no veremos ninguna imagen; en ese caso deberá indicar todos los nombres de dominio la siguiente forma :

:# evitar hotlinking de imagenes
SetEnvIf Referer ejemplo\.es localreferer
SetEnvIf Referer ejemplo\.com localreferer

    Order deny,allow
    Deny from all
    Allow from env=localreferer
    Header set Cache-Control max-age=3600


En este ejemplo he indicado dos nombres de dominio, pero puede indicar todos los que desee, siempre que se redirijan al sitio web que debe servir el contenido.

Espero que esta información te haya sido util.


Getting more girls into coding (Traer más chicas a la programación)

Solo dos opiniones para empezar a respirar de qué va esto...
Neelie Kroes: «Nuestras vidas ya son digitales, así que las generaciones más jóvenes, en particular, necesitan tener competencias digitales como la programación. En un futuro próximo esto será decisivo para conseguir empleos de calidad y esencial para la creación de una empresa».

Alja Isakovic: «La tecnología conforma nuestras vidas y no deberíamos permitir que una minoría decida cómo y para qué la utilizamos. Todos podemos hacer algo más que simplemente compartir y poner “me gusta”. Con la programación puedes dar vida a tus ideas y construir cosas que aporten alegría a los demás».

Si añadimos una variable más, como el reparto entre géneros que se observa entre los programadores, no creo que me equivoque si digo que estamos perdiendo gran parte del enfoque que pueden aportar las féminas. Profundizando más en el motivo, leo que 'no les interesa', 'es un mundo para chicos o frikis', otras razones pueden ser que ellas no se identifican en películas (siempre de secretarias, enfermeras, doctoras, abogadas, policias, investigadoras,...pero no en actividades relacionadas con la programación).
Los beneficios de aprender a programar son variados: desarrolla capacidades matemáticas, lógicas, de razonamiento, sociales, creatividad, cognitivos y emocionales. Para muchos es una actividad divertida y que nos proporciona gran satisfacción, y cualquiera puede beneficiarse de las habilidades que se desarrollan al aprender a programar. Además, se puede aplicar a otros conocimientos y destrezas en todos los aspectos de tu vida..

Quizá sea más sencillo ser un espectador y pulsar de vez en cuando 'me gusta'. Pero estamos perdiéndonos otra forma de programar y otra  visión diferente de la programación. 

En fin, para las que estén dispuestas a asumir esta apuesta existen soluciones, desde aqui os propono varios links:

www.agile-girls.com
PyLadies Meetup/
http://pyladies.meetup.com/es/cities/es/barcelona/

Semana de la Programación de la UE  octubre/2014


domingo, 26 de enero de 2014

Web en Apache con Python y WSGI (III) - Enrutador

Si has llegado hasta aquí sin pasar por el primer artículo 'Web en Apache con Python y WSGI (I)', no deberías continuar hasta leer los dos anteriores. Aunque todos los artículos son independientes en el tema a tratar, guardan una conexión con el objetivo hacia el que se dirigen PYTHON+MOD_WSGI+MYSQL+APACHE.

Introducción al concepto

Un "ENRUTADOR" de solicitudes web, tiene la importante tarea de gestionar qué respuesta se debe producir cuando se recibe una solicitud desde una página web. En la escueta definición de SOLICITUD se incluyen los clasicos FORM html, AJAX, REST,...y un largo etc. que ahora no corresponde detallar.

Esta pieza es fundamental en el desarrollo de un sitio web ( website ) que puede agrupar una gran cantidad de información y necesita responder dinámicamente según la solicitud que reciba de un cliente ( navegador, robot y otro tipo de programa o dispositivo).

Para desarrollar un 'ENRUTADOR' sencillo (para complicarse la vida, siempre hay tiempo...) estudiaremos que tipo de solicitudes podemos esperar y que tipo de respuestas vamos a producir.

Entradas y Salidas

Si te paras a pensar qué tipo de solicitudes puede hacernos un navegador cuando accede a nuestro sitio web, podríamos indicar algunas como :

http://www.prueba.com
http://www.prueba.com/cliente
http://www.prueba.com/cliente?codigo=9214&nombre=Jose%20Garcia      ( una tipica solicitud GET )
http://www.prueba.com/cliente/facturas/201400001

Además, HTTP define varias operaciones o verbos : GET, PUT, POST, DELETE que deberíamos tener en cuenta. Que pueden facilitarnos más información sobre qué tipo de operación debemos realizar con la solicitud, no olvidemos que junto a una solicitud, tambien podemos recibir datos.

GET  nos indica una solicitud de información o bien que se responda con una página web. Es el verbo por defecto.
PUT puede ser utilizado cuando nos solicitan que modifiquemos unos datos en una ficha de cliente. Por supuesto, la solicitud incluirá los datos a modificar y un código por el que deberíamos buscar al cliente.
POST puede ser utilizado cuando nos solicitan el crear un nuevo cliente.
DELETE puede ser utilizado cuando nos solicitan eliminar un cliente, siempre deberá incorporar los datos necesarios para identificar de una forma única al cliente a eliminar.

Observa que en cada información indico 'PUEDE SER UTILIZADO' porque esto es una convención, no una obligación.

También deberíamos tener en cuenta si nuestro sitio web responderá a solicitudes AJAX, creo que hoy día cualquier sitio web debe responder a solicitudes AJAX. Una solicitud AJAX transforma nuestra respuesta radicalmente.

Todo esto nos proporciona una valiosa información sobre qué se espera de 'ENRUTADOR'.

Definir un árbol de rutas posibles

 Observa el siguiente código :

# rutas ajax
mapper = routing.Map(default_subdomain='www',redirect_defaults='/')
mapper.add(rule='/dealer_ajax',controller="dealer.rest(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/user_ajax',controller="user.rest(environ['REQUEST_METHOD'].upper(),parametros)")
        mapper.add(rule='/manufacturer_ajax',controller="manufacturer.rest(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/supplier_ajax',controller="supplier.rest(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/customer_ajax',controller="customer.rest(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/customer_list_ajax',controller="customer.getList(parametros)")
mapper.add(rule='/product_ajax',controller="customer.get()")
mapper.add(rule='/user_auth_ajax',controller="user.auth(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/customer_auth_ajax',controller="customer.auth(environ['REQUEST_METHOD'].upper(),parametros)")
 

# rutas NO ajax
mapper.add(rule='/',controller="index()")

mapper.add(rule='/index',controller="index()")
mapper.add(rule='/admin',controller="admin()")
mapper.add(rule='/dealer',controller="dealer.get(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/user',controller="user.get(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/manufacturer',controller="manufacturer.get(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/supplier',controller="supplier.get(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/customer',controller="customer.get(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/customer_list',controller="customer.getList()")
mapper.add(rule='/customers',controller="customer.getList2()")
mapper.add(rule='/product',controller="product.get(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/user_auth',controller="user.auth(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/customer_auth',controller="customer.auth(environ['REQUEST_METHOD'].upper(),parametros)")
mapper.add(rule='/upload',controller="entity.rest(environ['REQUEST_METHOD'].upper(),parametros)")

Inicialmente defino una ruta por defecto, que respondería con una página inicial del sitio web.

Posteriormente voy incorporando distintas solicitudes posibles utilizando, el parámetro REQUEST_METHOD indica el tipo de verbo http GET, PUT, POST, DELETE y parámetros puede una estructura de datos :  list, dict o json.

mapper.add(rule=' solicitud posible ', controller=" función o clase.método que responderá ")

Además, es posible que con la solicitud nos encontremos con parámetros indeseables que el usuario haya introducido a mano en la linea de navegación y para los que no hayamos preparado el 'ENRUTADOR' :

http://www.prueba.com/customer?xyzk

Antes de comenzar a tratar la solicitud recibida, deberá eliminar los parámetros indeseables que pueden hacer que el ENRUTADOR fracase. En nuestro caso, solo he tratado de eliminar cualquier parámetro que aparezca detrás de un ? o %3F, si aún así, la ruta no existe por cualquier causa, devolvemos un código 404.

if environ['REQUEST_URI'].find('%3F')>0:
        __output = mapper.match(environ['REQUEST_URI'][0:environ['REQUEST_URI'].find('%3F')])
 elif environ['REQUEST_URI'].find('?')>0:
        __output = mapper.match(environ['REQUEST_URI'][0:environ['REQUEST_URI'].find('?')])
 else:
        __output = mapper.match(environ['REQUEST_URI'])
        # Si la ruta no existe devolvera 404 y podremos decidir tratarla o no


Pero suponga que lo que el cliente necesita con su solicitud es un documento que Vd. guarda en un repositorio del sitio web, en ese caso le puede interesar dejar que acceda a él libremente :

if __output == '404':
        # Realizar directamente la peticion no parametrizada en rutas
        no_mapper = eval("noMapper(environ['REQUEST_URI'])")
        if no_mapper == '404':
               # No existe la ruta parametrizada y tampoco el recurso
               __output = mapper.match('/')
               # evita codigo malicioso en eval {'__import__': None, '__builtins__': None}, {}
               output.append(eval(__output))
        else:
               # existe ruta directa al documento
               output.append(no_mapper)

Devolver una respuesta al cliente

Una vez tenemos todo parametrizado, solo queda generar la respuesta y eso lo conseguimos con :

if __output == '404':
       ...
else:
        if __output:
            try:
                 output.append(eval(__output))
            except Exception as e:
                 tb = sys.exc_info()[2]
                 LOG.debug("Error <%s> en linea %s !!!\n" % (str(e),tb.tb_lineno))



Por tanto, los datos que devuelva la función o clase.método, se incorporan a una estructura list denominada 'output'

Y esta estructura se devolverá acompañada de unos parámetros :

try:
        if len(output)>0:
            status = '200 OK'
            try:
                  output_len = len('\n'.join(output))
            except Exception as e:
                  tb = sys.exc_info()[2]
                  LOG.debug("Error <%s> en línea %s !!!\n" % (str(e),tb.tb_lineno))
            start_response(status, [('Content-type', 'text/html; charset=UTF-8'),
                                    ('Content-Length', str(output_len)),
                                    ('Set-Cookie',_set_cookie_.encode('utf8'))])
            return ' '.join(output)

Inicialmente comprobamos si 'output' contiene información, y posteriormente valora su longitud. Crea los datos para 'start_response' y devuelve ''.join(output).

En estas fechas ya he liberado la primera versión en desarrollo de PYMETRICK donde podrá encontrar un módulo ROUTING.py, además de otros muchos de los que hablaremos.

Conclusiones

Todo este código puede insertarse en el módulo tratado en el primer artículo Web en Apache con Python y WSGI (I), en el que nos referíamos a un fichero app.wsgi 

En el caso de las solicitudes AJAX, la respuesta debería contener una estructura de datos que posteriormente pueda interpretar y tratar javascript - en otra entrega escribiré sobre este particular - pero por el momento, puedes estar preguntándote ¿Dónde puedo comprobar si se trata de una solicitud AJAX ?, deberá comprobar si existe la variable HTTP_X_REQUESTED_WITH en el diccionario 'environ',  si no existe, señal de que no es una petición AJAX.

def  isAjax(self,environ):
        """AJAX, la variable HTTP_X_REQUESTED_WITH puede estar ausente en variables de entorno"""
        try:
                if environ.get('HTTP_X_REQUESTED_WITH').upper() == 'XMLHTTPREQUEST':
                    return 1
        except Exception as e:
                return 0