Translate

martes, 2 de octubre de 2018

Empresa y desarrollo de proyectos de software

Empresas en sus expresiones más sencillas como PYMES o autónomos, necesitan optimizar recursos y procesos para mejorar en su rendimiento.  Muchas necesidades empresariales ya cuentan con soluciones disponibles y necesitan adaptar la empresa a la operativa, y en muchos casos esta operativa puede ralentizar, cuando no bloquear, la gestión diaria.

Desarrollar proyectos con el fín de optimizar la gestión de una empresa, es una ventaja competitiva que permite reducir costes a medio plazo, además de dedicar más recursos al objetivo principal de la empresa, captar más ingresos.

Un factor muy importante es seleccionar el socio tecnológico que acompañará todo el proceso de desarrollo de proyectos. Contratar el desarrollo de un proyecto sólo es el principio de una etapa, a la que seguirán muchas más:  programación ad hoc, diseño y presentación del interfaz y respuestas bajo requerimientos específicos, la instalación, tests de usuarios para comprobar el grado de satisfacción, y mantenimiento técnico.

Recordemos que el proyecto debe ser un proceso iterativo, desarrollado mediante ensayo-error. La tendencia de los proveedores tecnológicos es trabajar al límite del plazo contratado, de forma que la rigurosidad metodológica se verá mermada en pro del cumplimiento del contrato. La experiencia nos dice que es mejor un proyecto que se toma su tiempo para llegar a la etapa de producción (time to market) que otro que, simplemente, se haga deprisa y corriendo.

Elegir la empresa a quien compremos el software implica conocerla, saber de su experiencia, de la del componente humano encargado de implementar el software, de su conocimiento de la herramienta y de su integración con nuestro sistema, porque normalmente, uno de los mayores problemas en todo tipo de proyectos tecnológicos es la integración de diversas herramientas y plataformas.

La inexperiencia de muchos gestores de PYMES, les lleva a buscar su solución preguntando a empresas similares en tamaño o gestión, podría asimilarse a la conocida escena de deshojar la margarita, conozco casos de desembolsos iniciales excesivos con expectativas incumplidas o excesivamente infladas por parte de los proveedores. A ningún gestor le gusta reconocer que ha errado en la selección o que no ha sabido encontrar nada mejor, por tanto, ante la insistencia y la desesperación de su interlocutor, te recomiendan su software.  

Otra cuestión es realizar el proyecto 'in house' por personal interno. Ganaremos en comunicación con los desarrolladores y conocimiento de las otras herramientas del sistema con los que se tiene que integrar , pero es importante también valorar las implicaciones en términos de coste de tiempo y recursos humanos, dinero al fin y al cabo.

La figura del FREELANCE con supuesta experiencia en areas concretas, o en lo que se presente según el caso (porque de algo tiene que vivir), está muy extendida actualmente, incluso es muy accesible contratar desarrollos con personas ubicadas en la otra punta del mundo subastando al mejor postor. Pero por muchas garantías que se obtengan, la comunicación puede ser una tortura y el desarrollo puede ser un desastre. Mi consejo es buscar desarrolladores cercanos, como las tiendas de barrio, siempre estará cerca, la comunicación no debe ser un problema sino un extra más del desarrollo. Obtener buenos resultados es cuestión de paciencia y comunicación hasta que duela. Lo digo por experiencia. 

No hay un modelo perfecto, sólo situaciones distintas y recuerda, un proyecto más caro no siempre está relacionado con más calidad ( investiga entre bambalinas de tus proveedores, te puedes llevar muchas sorpresas) .

domingo, 30 de septiembre de 2018

Ataques web y cómo gestionarlos

En todos los ámbitos de nuestros desarrollos existe una acción, que habitualmente realiza un cliente, y posteriormente desencadenamos una reacción como resultado. En situaciones controladas no se incluyen protecciones ante ataques, pero en el mundo real, donde existe la cyberdelincuencia a nivel global, cualquier proyecto está en el objetivo de los hacker - a veces para practicar, otras para conseguir otros objetivos -.

Un aspecto muy importante en mis proyectos - PYMETRICK es uno de ellos - es la seguridad ante los cyberataques, y nunca está de más invertir tiempo y recursos en mantenerse actualizado - tus clientes te lo agradecerán - . Muchos de estos ataques están muy extendidos en webs desarrolladas sobre PHP (WORDPRESS, JOOMLA, ...) y algunos consisten en enviar como parámetros : /?cmd=echo(344444443%2b1);  como es normal, el ataque tiene asociada una dirección IP que nos servirá para planificar nuestra reacción.

ABUSEIPDB https://www.abuseipdb.com es una base de datos online, que recolecta  IPs denunciadas por prácticas abusivas categorizadas como :

4   DDoS Attack
5   FTP Brute-Force    
6   Ping of Death  
7   Phishing   
8   Fraud VoIP 
9   Open Proxy 
10  Web Spam   
11  Email Spam
12  Blog Spam
13  VPN IP
14  Port Scan
15  Hacking    
16  SQL Injection
17  Spoofing   
18  Brute-Force
19  Bad Web Bot
20  Exploited Host
21  Web App Attack
22  SSH
23  IoT Targeted

Para utilizar el API de este recurso deberá registrarse y obtener una API KEY que posteriormente nos será de mucha utilidad en la respuesta reactiva que debemos desencadenar si comprobamos que la IP cliente se encuentra en esta BB.DD. 

Valentin Secades ha desarrollado un wrapper que podrás localizar en http://pipy o en https://github.com/vsecades/AbuseIpDb/ para PYTHON 2.7 y explica detalladamente su uso, para que de forma automática, puedas obtener los datos de cualquier IP que consultes.

En el caso de mis proyectos, todos han sido migrados a PYTHON 3 y sólo desarrollamos proyectos en esta versión, por lo que, una vez obtenido el código creado por Valentín Secades lo he adaptado para PYTHON 3 realizando muy pocos cambios (abuseipdb.py): 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
    codeauthor:: Valentin Secades

    check_ip
        abuseipdb.check_ip(ip="[IP]",days="[DAYS]")

    check_cidr
        abuseipdb.check_cidr(cidr="[CIDR]",days="[DAYS]")

    report_ip
        abuseipdb.report_ip(categories="[CATEGORIES]", comment="[OPTIONAL COMMENT]", ip="[IP]")

    Field                 Required     Default               Example                          Description
    [IP]                          Y               NA                 8.8.8.8                            IPv4 Address
    [DAYS]                   N               30                       30                              Check for IP Reports in the last 30 days.
    [CIDR]                    Y               NA              207.126.144.0/20             IPv4 Address Block in CIDR notation
    [CATEGORIES]     Y               NA                  10,12,15                       Comma delineated list of category IDs
    [OPTIONAL COMMENT]     N     NA     This is a comment.             Describe the type of malicious activity
    [API KEY]               Y              NA                 Tzmp1...quWvaiO         Your API key.

"""
import requests

class Parameters():

    configuration = {}
    url_templates = {
        "check_ip" : "https://www.abuseipdb.com/check/[IP]/json?key=[API_KEY]&days=[DAYS]",
        "check_cidr" : "https://www.abuseipdb.com/check-block/json?key=[API_KEY]&network=[CIDR]&days=[DAYS]",
        "report_ip" : "https://www.abuseipdb.com/report/json?key=[API_KEY]&category=[CATEGORIES]&comment=[COMMENT]&ip=[IP]",
    }
    defaults = {
        "days" : "30"
    }

    @staticmethod
    def get_config():
        return Parameters.configuration

    @staticmethod
    def set_config(config):
        Parameters.configuration = Parameters.dict_merge([Parameters.configuration, config])

    @staticmethod
    def merge_recursive(source, destination):
        """

        :rtype: dict
        """
        for key, value in source.items():
            if isinstance(value, dict):
                # get node or create one
                node = destination.setdefault(key, {})
                Parameters.merge_recursive(value, node)
            else:
                destination[key] = value

        return destination

    # taken from https://stackoverflow.com/questions/20656135/python-deep-merge-dictionary-data using recursion
    @staticmethod
    def dict_merge(list_dicts=[]):
        # print("List received: ")
        # pp = pprint.PrettyPrinter(indent=4)
        # pp.pprint(list_dicts)
        # Merge multiple dicts
        target = {}
        while len(list_dicts) > 0:
            temp_dict = list_dicts.pop()
            # pp.pprint(temp_dict)
            target = Parameters.merge_recursive(target, temp_dict)
            # pp.pprint(target)
        return target

def configure_api_key(api_key):
    #Check that api_key is not None OR that it has been set previously
    if api_key is not None:
        Parameters.set_config({"API_KEY": api_key})
    else:
        print("Api key cannot be blank")

def check_ip(ip=None,days=Parameters.defaults["days"]):
    #used to check an IP for reports
    if ip is not None:
        request_url = Parameters.url_templates["check_ip"]
        request_url = request_url.replace("[IP]",ip)
        request_url = request_url.replace("[API_KEY]",Parameters.get_config()["API_KEY"])
        request_url = request_url.replace("[DAYS]",days)
        print(request_url)
        response = requests.get(request_url)
        # return raw for now, we will add decorators later on
        return response.text
    else:
        print("ip is not defined")
        return None

def check_cidr(cidr=None,days=Parameters.defaults["days"]):
    # used to check an IP for reports
    if cidr is not None:
        request_url = Parameters.url_templates["check_cidr"]
        request_url = request_url.replace("[CIDR]", cidr)
        request_url = request_url.replace("[API_KEY]", Parameters.get_config()["API_KEY"])
        request_url = request_url.replace("[DAYS]", days)
        print(request_url)
        response = requests.get(request_url)
        # return raw for now, we will add decorators later on
        return response.text
    else:
        print("CIDR is not defined")
        return None

def report_ip(categories=None, comment="", ip=None):
    # used to check an IP for reports
    if ip is not None and categories is not None:
        request_url = Parameters.url_templates["report_ip"]
        request_url = request_url.replace("[IP]", ip)
        request_url = request_url.replace("[API_KEY]", Parameters.get_config()["API_KEY"])
        request_url = request_url.replace("[COMMENT]", comment)
        request_url = request_url.replace("[CATEGORIES]", categories)
        print(request_url)
        response = requests.get(request_url)
        # return raw for now, we will add decorators later on
        return response.text
    else:
        print("ip is not defined")
        return None










Ejemplo de cómo usar esta libreria :

import abuseipdb
import json

if __name__ == '__main__':
    ip_addr = "77.95.35.23"     # IP identificada en ataques
    #ip_addr = "1.1.1.1"           # IP NO identificada en ataques
    abuseipdb.configure_api_key("API KEY")
    rp =abuseipdb.check_ip(ip=ip_addr)
    try:
        print(type(rp).__name__)
        json_rp = json.loads(rp)
        if len(json_rp)>0:
            if json_rp[0]['ip'] == ip_addr:
                print(json_rp[0]['isWhitelisted'])
                print(json_rp[0]['isoCode'])
        print(rp)
    except ValueError:
        pass


Observa que el parámetro que nos indicará si es una IP identificada en procesos de faude o ataques nos los indicará el parámetro 'isWhitelisted' (False para IPs ATACANTES y True para IPs no registradas en ABUSEIPDB), el parámetro 'isoCode' solo nos servirá como referencia del país al que pertenece la IP. Integrar este proceso en su desarrollo web no será muy complicado y podrá redirigir la solicitud a una página de error o a un punto muerto donde no exista posibilidad de continuar con un ataque.

Todo el código y ejemplo están disponibles en https://github.com/JAVTAMVI/AbuseIpDb3

Fiel a nuestra política de publicar artículos prácticos y sencillos, espero que le ayude a enfrentarse a situaciones de ataque.