Translate

Mostrando entradas con la etiqueta WEB. Mostrar todas las entradas
Mostrando entradas con la etiqueta WEB. Mostrar todas las entradas

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.


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







jueves, 5 de septiembre de 2013

PYMETRICK comienza a ser una realidad...

Todo proyecto comienza con una idea, a veces despues de aglutinar muchas pequeñas ideas y sugerencias.

Somos incorformistas por naturaleza, aunque en algún caso se haya olvidado. Buscamos pero siempre hay un pero..., puede servir lo que hay, pero le falta..., no tengo nada mejor...pero en cuanto pueda. Esta idea surgió de todas las ideas que nunca encontraban un refugio estable.

Un largo camino, muchos libros, ayuda desinteresada ( que agradezco infinitamente a Eugenia Bahit por sus magníficos artículos y cursos ) y mucho esfuerzo, han comenzado a dar respuestas. Ahora solo debe crecer algo más y comenzará a andar solo ( con licencia GPL debajo del brazo ) es lo menos que puedo hacer en respuesta a toda la ayuda recibida.

Todos necesitamos un Pepito Grillo que nos haga aterrizar cuando perdemos el camino...César gracias. Marcarnos objetivos y cumplirlos, que no es poco.

Hoy comienza a funcionar PYMETRICK formado por código desarrollado con un objetivo muy claro: proporcionar productos sencillos, parametrizables, con fácil mantenimiento. Todavía nos queda camino, pero el que pisamos ahora es de asfalto, podremos ir más veloces apostando por la seguridad. Con la versión 0.1 sería demasiado pretencioso ponerlo a disposición de la comunidad PYTHON, pero ese es nuestro objetivo.

¿ Que todavía no sabes qué es PYMETRICK ?  Un motor que nos permite desarrollar proyectos WEB/SOCKET (ecommerce, blog's, formación, publicación de noticias - periódicos - ) pensando en todos los dispositivos disponibles, pero de una forma sencilla - aunque dirigido a programadores - ¿crees que es pretencioso? Seguramente sí, pero si no ponemos metas más lejos, nos quedaremos más cerca de nada.

Como el movimiento se demuestra andando, ya estamos caminando, espero que el camino nos lleve donde queremos.