security-api

Security API – Dale a tus empleados lo que necesitan (de forma segura)

Hace poco vimos en SecurityInside consejos a tener en cuenta para revisar la seguridad de tu API. Como comentamos, ofrecer servicios de información en el mundo de hoy si no tienes API, te da una desventaja considerable que podría terminar por arruinar tu negocio.

Es por eso que tu departamento de desarrollo debe tener en mente trabajar en una buena api (sencilla y segura) que de servicio tanto a clientes como a las plataformas y herramientas internas.

Hay muchas formas de desarrollar APIs, pero en mi caso me he decantado últimamente por Flask para Python sobre Elastic Beanstalk de Amazon Web Services.

Security API, ¿por qué?

Cada vez que arranco un proyecto de seguridad con una nueva empresa, me presento desde el punto de vista de vida laboral. Cuento mis años como becario, desarrollador, jefe de proyecto, consultor y luego el paso al mundo de la seguridad. Me miran como diciendo «¿para qué todo este rollo?». Sencillo, es la forma de introducir que soy un apasionado de la seguridad y que mi objetivo principal es asegurar los activos de la empresa, pero siempre tratando de hacer la vida sencilla a los compañeros que tienen que lidiar con todas las medidas y controles de seguridad que se implanten.

Conozco profesionales de la seguridad que nunca se han manchado las manos picando código y que aplican medidas maravillosas que fortifican los activos mientras complican el trabajo diario. Lo importante es la seguridad, pero mi perfil de desarrollador me hace pensar siempre en hacerlo de forma que ellos tengan todas las facilidades para trabajar. Seguros, pero trabajando.

Por todo esto, hace no mucho empecé a dar vueltas a la idea de crear una «security api» que diera servicio a determinadas necesidades de los compañeros de los diferentes departamentos. De esta forma, aplico medidas de seguridad generales y les doy la posibilidad de solicitar ciertos permisos, accesos, … de forma automática en base a roles.

¿Cómo funciona?

Básicamente todo funciona en base a usuarios que se autentican con login, password y 2fa (Google Authenticator, Android, iOS). Cada usuario tiene un rol y subrol con el que se indica qué cosas tienen permitidas y todo se gestiona mediante JSON Web Tokens.

Flask api template

Os he dejado en Github un nuevo repositorio que contiene el esqueleto de una api desarrollado en Python utilizando la biblioteca Flask.

Podéis lanzarlo en local para desarrollo y hacer pruebas de forma sencilla utilizando Postman, ya que os he dejado también un fichero con ejemplos de uso. Tendréis queja… 😀

Vamos viéndo cosillas

En el fichero principal (flask_api.py) veréis unas cuantas cosas que os voy a ir explicando.

Al principio, tenéis una función que se lanza tras arrancar el server, ahí se pueden configurar diferentes cosas (dar de alta la ip del server en determinados grupos de seguridad, enviar notificaciones, …):

##########################################################################################
# This function makes initial tasks.
##########################################################################################
@application.before_first_request
def _run_on_start():

    if not DEBUG:
        print "Flask started!"
        
        # Here you can do some initial tasks

##########################################################################################

Después se incluyen dos apartados para la gestión de códigos 404 y 405:

##########################################################################################
# This function returns happy 404.
##########################################################################################
@application.errorhandler(404)
def page_not_found(e):
    
    return redirect("https://goo.gl/LuKygx", code=302)

##########################################################################################

##########################################################################################
# This function returns happy 405.
##########################################################################################
@application.errorhandler(405)
def method_not_allowed(e):
    
    ret_html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>'
    ret_html = ret_html + '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es" lang="es">'
    ret_html = ret_html + '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'
    ret_html = ret_html + '<title>Flask API SecurityInside.info Template</title>'
    ret_html = ret_html + '<style type="text/css" media="screen">'
    ret_html = ret_html + 'h1 {text-align:left; color: #444;}'
    ret_html = ret_html + '</style></head><body>'
    ret_html = ret_html + '<h1>Flask API <a href="http://www.securityinside.info" target="blank">SecurityInside.info</a> Template</h1>'
    ret_html = ret_html + 'Method not allowed or missing params, check API doc to solve the problem.'
    ret_html = ret_html + '</body></html>'

    return flask_api_functions.ret_content_type(ret_html, 'html', 405) 

##########################################################################################

Una siempre interesante función ping para comprobar rápidamente si el server está vivo:

##########################################################################################
# This function shows API alive info. 
##########################################################################################
@application.route('/' + API_VERSION + '/ping', methods=['GET'])
def ping():
    
    response = {'code': 200, 'message': 'pong'}    
    return flask_api_functions.ret_content_type(response, 'json', 200)

##########################################################################################

La parte más interesante del ejemplo, la gestión del login y creación del token:

##########################################################################################
# This function checks auth user and return JWT.
##########################################################################################
@application.route('/' + API_VERSION + '/login', methods=['POST'])
def login():
    
    # Check preconditions
    ##################################################################################
    response = flask_api_functions.check_preconditions(request, False)
    
    if (response['code'] != 200):
        return flask_api_functions.ret_content_type(response, 'json', response['code'])
    ##################################################################################
    
    # Check for user credentials
    ##################################################################################
    response = flask_api_functions.check_auth(request)
    return flask_api_functions.ret_content_type(response, 'json', response['code'])
    ##################################################################################

##########################################################################################

Y la implementación concreta dentro del fichero flask_api_functions.py:

##########################################################################################
# This function checks user authorization header request.
#
# @param request - User request data.
#
# @return Formatted response.
# 
##########################################################################################
def check_auth(request):
    
    try:
        auth = base64.b64decode(request.headers['Authorization'].split()[1])
    
    except:
        response = {'code': 401, 'message': 'Invalid basic authorization header.'}
        return response

    if len(auth.split(':')) != 2:
        response = {'code': 401, 'message': 'Invalid basic authorization header.'}
        return response
    else:
        """
        Here you have to check user credentials and optional totp value.
        """

        # If user credentials is ok, then generate jwt
        if(True): 
            
            # User with valid credentials
            token = jwt.encode({
                'user_id': '<insert_here_user_id>',
                'user_name': '<insert_here_user_name>',
                'user_roles': '<insert_here_user_roles>',
                'other_fields': '<insert_here_other_fields>',
                'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)}, 
                SECRET)
                                
            response = {'code': 200, 'message': None, 'token': token}
            return response

        # If user credentials is not ok, then return error message
        else:
            response = {'code': 401, 'message': '(d\'oh!) Wrong user, password, totp value or maybe user is not active.'}
            return response

##########################################################################################

Para entender ciertas cosas como la forma de enviar el header «Authentication», pásate por el README.md del repositorio y ejecuta las pruebas con postman, verás que es la mar de sencillo.

El ejemplo es sólo parte del script completo. Si quieres utilizarlo o modificarlo, puedes descargarlo desde nuestro repositorio en GitHub.

 

Importante antes de terminar

Flask no está preparado para funcionar directamente en un entorno de producción, para hacerlo tienes que tener en cuenta un par de cosas (está todo en la documentación del repositorio).

En mi caso particular, ya os he dicho que lo tengo montado en Elastic Beanstalk de Amazon Web Services, si queréis os puedo contar cómo lo he hecho en otra entrada.

Por supuesto, si tenéis alguna duda sobre esta entrada, preguntad que estaré encantado de echar una mano.

Saludos!

SecurityInside Live: Check Point Cyber Day 2017

La omnipresente transformación digital y el Internet de las Cosas piden cambios fundamentales en el presente y futuro de la ciberseguridad. Los últimos ataques ocurridos a nivel mundial demuestran la necesidad de implementar estrategias innovadoras para securizar las empresas.

Hoy asistimos al Check Point Cyber Day 2017, una jornada dedicada a los CIOs, CISOs, IT Security Managers y todos los interesados en conocer el futuro de la ciberseguridad de la mano de expertos nacionales e internacionales en esta materia.

Leer más

sftp

sFTP en AWS EC2 en 5 minutos

En algún momento te puedes encontrar con la necesidad de levantar un sFTP para que alguien te envíe ficheros. Yo lo he necesitado hace poco y lo he solucionado rápidamente con una instancia de AWS EC2. Te dejo paso a paso lo que tienes que hacer por si lo necesitas.

Leer más

micro_focus_summit17

SecurityInside Live: Micro Focus Summit 2017

El éxito de mañana está vinculado a la respuesta que hoy se dé a los desafíos de seguridad de nuestro negocio. Por eso, Micro Focus comparte en su Summit 2017 las experiencias de sus clientes y el conocimiento de su equipo.

Leer más

pruebas-de-seguridad

App terminada: ¿la subo a producción o hago pruebas de seguridad?

Hace dos días me tocó ir a la ventanilla de Alsa para cerrar un billete de vuelta. Sí, a la ventanilla. Mis argumentos sobre que puedo comprar hamburguesas del mercado con Amazon Prime, la comida del mes en Carrefour, pantalones vaqueros con Vibbo, pedir «taxi» con Uber o «conocer amigas» con Tinder… todo desde mi móvil, no fueron suficientes para convencer a la simpática persona que me atendía de que no tenía sentido que no pudiera hacerlo por internet a estas alturas de siglo.

Hoy en día todas las empresas que quieren estar «a mano» de sus usuarios invierten en apps móviles o web (responsive). Es imprescindible que sean sencillas,  rápidas y con un diseño fabuloso.

Seguro que alguno ya lo ve venir… ¿reconocéis lo que falta?

Seguridad. Cerca del 70% de las vulnerabilidades se encuentran en la capa de aplicación y apenas el 10% de los desarrolladores reconocen haber realizado pruebas de seguridad antes, durante y tras el lanzamiento de las apps [1].

Tampoco es para tanto…?

Quizás parezca que no, pero un error en una aplicación puede derivar en graves violaciones de seguridad. Lo que para un desarrollador puede ser un pequeño fallo, puede ser la entrada a la red interna de la organización y convertirse en el principio de una serie de escalados en horizontal y vertical que terminen con un magnífico acceso de administrador.

Una aplicación que no ha pasado las necesarias pruebas de seguridad puede terminar costando millones al negocio. Todavía me acuerdo de aquella charla de Antonio Ramos (www.leetsecurity.com) en la RootedCON titulada ¿Y si la seguridad afectara al valor contable de la empresa?. Que le pregunten a Yahoo si afecta a las cuentas o no un fallo de seguridad…

Si no te preocupas por la seguridad, quizás deberías preocuparte por la tesorería… ¿Qué tal unas recomendaciones mínimas?

Versiones cerradas, no metas parches de última hora

A veces, con las prisas, metemos características que no están del todo maduras. Tienen que estar aunque no estén demasiado finas, que «ya lo iremos arreglando». La realidad es que se suele dejar como está, ya que tras la release, una lista de nuevas features está en tu nuevo sprint y no tienes tiempo para arreglar cosas.

Si algo no está pulido, probado y validado, no debería ponerse en producción, a la larga lo agradecerás.

Pruebas de todo tipo (sí, de seguridad también)

Una batería de pruebas es esencial en el desarrollo, pero no hablo de las pruebas funcionales mínimas que realiza el desarrollador conforme escribe código. Haz todo tipo de pruebas, automatiza, audita tu código, incluso busca expertos en QA testing que te enseñen cómo hacerlo.

Nunca dejes de revisar

Las pruebas no deben terminar una vez se publica una versión. Si tienes algo en producción durante mucho tiempo, ponlo a prueba, «putéalo» para que seas tú el primero en encontrar una vulnerabilidad y no el primero en sufrirla.

 

No es tan complicado, no te olvides las pruebas y tus usuarios, tu negocio y tu tesorería te lo agradecerán.

[1] https://www.sans.org/reading-room/whitepapers/analyst/2015-state-application-security-closing-gap-35942