Archivo de la etiqueta: soluciones

Docker 101 #2: puertos y volúmenes de un contenedor

docker-image

En el artículo anterior comenzamos con una breve introducción a docker, vimos su instalación, configuración e incluso lanzamos un par de servidores web nginx usando contenedores, en esta ocasión explicare un poco más acerca de los puertos y los volúmenes.

Puertos

Ok, lo primero que explicare será el mapeo de puertos, abrimos una terminal y ejecutamos el siguiente comando:

$ sudo docker run --name servidor-web -p 80:80 nginx

El parametro –name sirve para asignarle un nombre al contenedor.

El parámetro -p sirve para realizar el mapeo de puertos y recibe una cadena en el formato PUERTO-HOST:PUERTO-CONTENEDOR, es decir, del lado izquierdo definimos el puerto que nuestro sistema operativo le asignara al contenedor de docker y del lado derecho el puerto en el que realmente se ejecuta el servicio dentro del contenedor, en este caso nginx (suena un poco confuso al inicio así que regresa y léelo de nuevo hasta que lo entiendas)

En el comando anterior estamos mapeando el puerto 80 de nuestra computadora con lo que sea que este corriendo en el puerto 80 del contenedor, es por eso que si vamos a http://localhost veremos el servidor web en ejecución 🙂

nginx

En la consola desde donde ejecutaste el comando podrás ver las peticiones hechas al servidor dentro del contenedor.

docker-cli

Al ejecutar el comando y correr el contenedor abras notado que la consola se queda bloqueada por el servidor web, para evitar eso podemos correr el contenedor en modo detach con el parámetro -d, esto ejecutara el contenedor en segundo plano.

$ sudo docker run -d --name servidor-web -p 80:80 nginx

docker_detach

Observa como tan pronto como ejecutamos el comando docker nos devuelve el control de la terminal, cuando ejecutas contenedores de esta forma no olvides que para eliminarlos primero tienes que recuperar su id, el cual puedes obtener haciendo:

$ sudo docker ps

y en la primera columna encontraras el ID del contenedor que después deberás de eliminar usando sudo docker rm [CONTAINER-ID], si lo prefieres un tip muy útil para borrar todos los contenedores que hayas creado es ejecutar:

$ sudo docker stop $(sudo docker ps -a -q)
$ sudo docker rm $(sudo docker ps -a -q)

El primer comando detiene todos los contenedores que estén en ejecución y el segundo los elimina todos (no puedes eliminar un contenedor que este en ejecución).

Puedes correr todas los contenedores que quieras (o necesites) de nginx en diferentes puertos y con diferentes nombres y cada uno será una instancia completamente diferente del servidor web 🙂
containers

Observa como cada uno de los servidores web corren en un puerto diferente.

multi-docker

Volúmenes

Los volúmenes en docker pueden ser definidos con el parámetro -v y nos ayudan a resolver el problema de la persistencia de datos en los contenedores, un volumen puede ser visto como un mapeo entre un directorio de nuestra computadora y un directorio en el sistema de archivos del contenedor, regresemos a nuestro contenedor de nginx, ¿cómo le hacemos para mostrar un sitio web en nginx en lugar de la página por default?

Lo primero que haremos será crear una carpeta en donde colocaremos el código fuente de nuestro sitio web html (por ahora no trabajaremos con nada dinamico), por ejemplo website

website

Ejecutamos el siguiente comando mapeando el contenido de /home/alevsk/dev/sitio-web hacia /usr/share/nginx/html que es el directorio por default que utiliza nginx para servir contenido a Internet.

$ sudo docker run -d --name sitio-web -v /home/alevsk/dev/sitio-web:/usr/share/nginx/html -p 80:80 nginx

La próxima vez que visitemos http://localhost/ veremos nuestro sitio web corriendo.

nginx-web

Puedes replicar este contenedor con el contenido del sitio web tantas veces como quieras, es muy util en un escenario donde necesitas varios ambientes para pruebas, desarrollo, etc.

Eso es todo por ahora, en el siguiente tutorial aprenderemos a crear nuestras propias imágenes de docker (dockerizar aplicaciones), después de eso veremos otra herramienta bastante útil llamada docker-compose para facilitar la orquestación de aplicaciones que utilizan múltiples contenedores.

Saludos y happy hacking.

Análisis interno y de seguridad de la plataforma #CiudApp

26757261694_a0273fdcb6_z

El 30 de mayo de este año (2016) Enrique Alfaro, presidente de Guadalajara, anunció durante la sesión de Campus Night la nueva aplicación de administración y gestión de información relevante para los ciudadanía: CiudApp.

Entre muchas otras cosas, la aplicación permite a los ciudadanos estar informados de las noticias más relevantes de la administración (por eso del trending de los gobiernos abiertos), realizar reportes incluyendo geolocalización y solicitar o proponer servicios al gobierno.

A raíz del lanzamiento de la aplicación ha surgido bastante debate y comentarios en Internet en torno a la misma, incluso leí un artículo muy interesante en donde le hacían un análisis de usabilidad a la aplicación, pienso que está bien ya que en nuestro querido México, en el pasado hemos tenido casos polémicos como el de la app de los 115 millones de pesos y es natural que la gente que sabe del tema (desarrolladores, ux designers, community managers, etc.) emitan su opinión acerca de un producto tecnológico “generado” por el gobierno.

Regresando a lo anterior, después de leer el análisis heurístico de usabilidad de la aplicación me pregunte a mí mismo ¿Quién está detrás del desarrollo de la aplicación?, ¿Y si le hacemos un análisis interno a la app? Podemos hacer algunas pruebas de seguridad también, vamos a “destripar” la aplicación para entender como está construida y ver cómo fue desarrollada, todo esto con la premisa de que no vamos a romper ni a explotar / vulnerar / hackear nada 🙂 y pues manos a la obra.

Instalando la aplicación en genymotion

Lo primero que vamos a hacer es descargar el apk de la aplicación (Si, el análisis lo vamos a hacer sobre un dispositivo con Android), el link de la app CiudApp en la playstore es https://play.google.com/store/apps/details?id=com.radmas.iyc.guadalajara.mex y hay miles de sitios web que te permiten descargar el apk utilizando la url de la tienda, solo busquen algún en google y descarguen el archivo, cuando hayan terminado tendrán un archivo llamado más o menos así Ciudapp_Guadalajara_v3.0.146_apkpure.com.apk.

Después de eso vamos a instalar la aplicación en genymotion, si no saben lo que es pueden investigar y descargarlo de acá https://www.genymotion.com/ y probablemente escriba un tutorial en el futuro acerca de cómo instalarlo y configurarlo.

android

Ejecutamos nuestro dispositivo virtual de con Android y vamos a proceder a instalar el apk de la aplicacion mediante adb con el comando:

adb install Ciudapp_Guadalajara_v3.0.146_apkpure.com.apk

adb

Una vez termine el proceso de instalación tendremos la app en nuestro dispositivo virtual.

installed

Bueno, ya tenemos la aplicación preparada, vamos a dejar esto por un momento y vamos a regresar a la carpeta en donde descargamos nuestra apk, ahora vamos a utilizar 3 herramientas, apktool, dex2jar y Jd (Jar Decompiler) para entender un poco más como está construida la app.

Obteniendo los assets de la aplicación

Vamos a utilizar apktool para de compilar la app y leer así algunos archivos importantes como el AndroidManifest.xml, strings.xml y cualquier otro archivo que nos pueda decir algo acerca de la app o sus desarrolladores.

apktool.bat d "C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\Ciudapp Guadalajara_v3.0.146_apkpure.com.apk"

d

apktool_d_folder

Entre los permisos que nos solicita la aplicación tenemos cosas típicas como acceso a la cámara, acceso al GPS, escribir y leer en almacenamiento externo, acceso a internet, etc., permisos que uno esperaría de una aplicación como esta.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.radmas.iyc.guadalajara.mex" platformBuildVersionCode="23" platformBuildVersionName="6.0-2166767">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <permission android:name="com.radmas.iyc.guadalajara.mex.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
    <uses-permission android:name="com.radmas.iyc.guadalajara.mex.permission.MAPS_RECEIVE"/>
    <uses-feature android:glEsVersion="0x20000" android:required="true"/>
    <permission android:name="com.radmas.iyc.guadalajara.mex.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
    <uses-permission android:name="com.radmas.iyc.guadalajara.mex.permission.C2D_MESSAGE"/>
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="com.radmas.iyc.guadalajara.mex.permission.GPSTRACKER"/> //Show custom dialgo above Statusbar
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>

Lo que me llamo la atención desde un inicio, y una de las razones por las que quería hacer este ejercicio, fue encontrar que empresa fue la desarrolladora de la app, revisando más a fondo el archivo vemos que muchos de los packages empiezan con com.radmas.iyc.guadalajara.mex, haciendo una búsqueda en google de radmas.com encontramos lo que parece ser una agencia española (pueden revisar su información en who.is) de desarrollo de software especializada en diseño, marketing, creación de aplicaciones móviles y páginas web.

radmas

Si son desarrolladores de Android pueden identificar rápidamente otras cosas interesantes como los activities de la aplicación, uno que otro broadcast receiver, algunos servicios, intents, public api keys, etc.

También me llamo la atención que la aplicación tiene internacionalización, aunque la app parece un poco genérica, está bien hecha en ese aspecto

idiomas

Con la utilidad File Locator Lite también encontramos algunos archivos interesantes como:

app.json

{
	"server_url":"https://api.mejoratuciudad.org",
	"base_uri":"",
	"jurisdiction_id":"mx.guadalajara",
	"api_key":"12",
	"name":"Ciudapp Guadalajara",
	"promo_text": "Echa un vistazo a 'Ciudapp Guadalajara', la mejor aplicación móvil de atención ciudadana https://play.google.com/store/apps/details?id=com.radmas.iyc.guadalajara.mex",
  	"promo_web": "https://play.google.com/store/apps/details?id=com.radmas.iyc.guadalajara.mex",
  	"email": "[email protected]",
  	"languages":    [{"key":"Español","value":"es"},
	{"key":"Inglés","value":"en"}],
  "share_url": "http://intranet.mejoratuciudad.org/mail/request/"
}

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="enviroments">
        <item>http://api-canary.mejoratuciudad.org</item>
        <item>http://api-developer.mejoratuciudad.org</item>
        <item>http://api.mejoratuciudad.org</item>
        <item>http://open010.valvaro.lan</item>
        <item>http://open010.vivan.lan</item>
        <item>http://open010.vnacho.lan</item>
        <item>http://open010.vfernando.lan</item>
        <item>http://open010.vwalter.lan</item>
        <item>http://open010.vemilio.lan</item>
    </string-array>
</resources>

De acuerdo a lo que vemos la app se comunica con varios servicios web hospedados en mejoratuciudad.org, investigando un poco sabemos que la empresa anterior mencionada (radmas.com) también es dueña de este producto, y según sus propias palabras:

Mejora Tu Ciudad (MTC) es una plataforma de comunicación entre los ciudadanos y el ayuntamiento, que se enmarca dentro de las soluciones Smart City y que se basa en los tres pilares fundamentales de Open Government:

Participación, colaboración y transparencia

Al menos con esto estoy un poco más tranquilo sabiendo que el gobierno de Guadalajara contrato una empresa especializada para el desarrollo de la aplicación y no a un par de becarios para programar la app XD.

mejoratuciudad

Del DEX al JAR y del JAR al código fuente

Antes de avanzar al siguiente paso hay algo más que podemos hacer, es sabido que los archivos apk, son en realidad archivos rar, entnces tomamos nuestro Ciudapp Guadalajara_v3.0.146_apkpure.com.apk lo renombramos a Ciudapp Guadalajara_v3.0.146_apkpure.com.rar y lo descomprimimos obteniendo algo como lo siguiente:

dex2jar

El contenido de la carpeta es muy parecido a lo que generamos cuando utilizamos apktool sin embargo si tratan de abrir algún archivo xml aquí, como por ejemplo el AndroidManifest.xml lo unico que obtendrán serán 0s y 1s, pero tenemos algo interesante, el archivo classes.dex.

Podemos utilizar dex2jar con el siguiente comando para generar un archivo jar (Javar Archive)

dex2jar.bat "C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\Ciudapp Guadalajara_v3.0.146_apkpure.com\classes.dex"

Obtendremos un archivo classes_dex2jar.jar que podemos abrir en el Java decompiler, tan solo abrimos la aplicación y seleccionamos el archivo jar.

jd

Muchos de los paquetes que vemos ahí son librerías de terceros y conforme indagamos más y más podemos ver que la aplicación utiliza librerías de facebook, google, librerías para animaciones, reyclerviews personalizados, analytics, Bitly, crash analytics, etc.

jd2

Para agilizar la búsqueda podemos seleccionar File > Save All Sources y elegimos una carpeta en donde guardaremos el archivo Zip generado que después descomprimimos y analizamos, nuevamente utilizando File Locator Lite

app_decompiled

Algunas búsquedas nos arrojan cosas interesantes, como por ejemplo credenciales en texto plano XD

password

passwords2

username

Con las credenciales obtenidas fue posible acceder a lo que parece un portal administrativo de estacionamientos de una tercer empresa llamada urbiotica en http://services.urbiotica.net/

urbiotica

Podemos ver, al parecer, en tiempo real el status de los cajones de un estacionamiento en algún lugar de España, la pregunta aquí es: ¿Que hacían esas credenciales hardcodeadas en la app?, navegando la aplicación no veo por ningún lado algo que haga referencia a estacionamientos sin embargo haciendo otra búsqueda con la palabra park obtenemos referencias en muchos otros archivos.

park

Sin embargo no pude lanzar ningún activity o servicio relacionado al Parking desde adb shell ya que no estaban registradas en el AndroidManifest.xml.

Opino que esas credenciales son quizás de algún desarrollo anterior, el código fuente fue re utilizado y las credenciales fueron olvidadas ahí y ahora están siendo distribuidas masivamente XD, en fin la pantalla administrativa del sistema de estacionamientos luce de esta forma, tiene sentido que haya otra empresa más involucrada en este proyecto, sobre todo si es está enfocada en tecnologías para Smart cities.

park2

Revisando los archivos de la aplicación

Regresamos a nuestro emulador y ahora vamos a abrir una shell de windows y ejecutamos el comando:

adb shell

El comando anterior nos entregara una consola para poder navegar el dispositivo donde instalamos la app, conociendo un poco sobre Internals y arquitectura general de Android sabemos que la información de una app se almacena en la ruta /data/data/package.de.la.app, en este caso /data/data/com.radmas.iyc.guadalajara.mex/

shared_pref

Aquí encontramos otros archivos que podrían contener más cosas interesantes y que a su vez nos podrían enseñar un poco más cómo funciona la aplicación, comenzamos por ver que hay dentro de la carpeta shared_prefs, si no saben lo que es pueden investigar más al respecto acá https://developer.android.com/training/basics/data-storage/shared-preferences.html pero en resumen es un método de almacenamiento persistente que nos ofrece el sistema operativo, la información es almacenada en archivos XML y es generalmente utilizado para guardar configuraciones de la aplicación.

shared_prefs2

Quizás el archivo más interesante es JURISDICTION_LOADED.xml que contiene:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="mx.guadalajara">mx.guadalajara</string>
</map>

Y del cual vamos a hablar en la siguiente etapa. Después de que se loguen en la app pueden ver que nuevos archivos de preferencias compartidas son creados, estos contienen información relacionada con su cuenta, facebook, etc, etc.

shared_prefs3

En la carpeta /data/data/com.radmas.iyc.guadalajara.mex/databases/ encontramos dos bases de datos sqlite, procedemos a descargarlas para ver qué es lo que contienen usando los siguientes comandos.

adb pull /data/data/com.radmas.iyc.guadalajara.mex/databases/DBImproveYourCity C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\databases

adb pull /data/data/com.radmas.iyc.guadalajara.mex/databases/DBImproveYourCity-journal C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\databases

Y después podemos abrirlas utilizando cualquier visor de sqlite como por ejemplo SQLiteBrowser

db2

En la tabla request es donde podemos ver los reportes “cacheados” por la aplicación en nuestro teléfono, podemos observar el id del reporte (service_request_id), el id de la cuenta de la persona que hizo el reporte (account_id), la calle, coordenadas, url de imagenes, descripcion, status, fecha, etc.

db4

db5

db6

En general cumplen con las buenas prácticas de almacenamiento de información, no almacenan datos sensible en lugares inseguros como la SD externa y en su lugar lo hacen en bases de datos SQLite y shared preferences que es donde solo la aplicación tiene privilegios de lectura y escritura.

Entendiendo las comunicaciones

Ahora viene una de las partes más divertidas de este tipo de ejercicios, saber cómo y con quien se comunica la aplicación, que es lo que envía y que es lo que recibe, etc. Pero antes, si están haciendo este ejercicio mientras leen mi artículo se darán cuenta que la app maneja un concepto muy interesante llamado Jurisdicciones o Jurisdiction, después de analizar el código fuente y visualizar en mi mente el flujo de información, jurisdiction es un identificador que le dice a la app que información cargar de acuerdo a una región especifica, por default la app al iniciar hace un request a https://api.mejoratuciudad.org/applications/12.json y obtiene como resultado

{
  "name": "Guadalajara",
  "active": true,
  "default_jurisdiction_id": "mx.guadalajara"
}

Pueden jugar con el numero al final de la url y de esta manera nos damos cuenta que esta aplicación (o al menos el backend y los servicios web) son escalables y han sido implementados en muchas otras ciudades y países (¿Posiblemente otros clientes de radmas?), por ejemplo https://api.mejoratuciudad.org/applications/5.json nos arroja:

{
  "name": "Cuenta conmigo",
  "active": true,
  "default_jurisdiction_id": "org.sevilla"
}

https://api.mejoratuciudad.org/applications/4.json

{
  "name": "Greencities",
  "active": true,
  "default_jurisdiction_id": "eu.greencities.malaga"
}

Revisando rápidamente con una herramienta para automatizar el proceso podemos ver que actualmente existen 16 jurisdicciones diferentes (al menos en ese servidor), podemos encontrar muchísimas ciudades más del estado de Jalisco así como ciudades de otros países, eso nos dice que la aplicación está siendo implementada en muchos más lugares 🙂

burp

El mismo ID de jurisdicción es utilizado para obtener las noticias, reportes y servicios relacionados de una región en específico.

Continuamos, como sabemos la aplicación se comunica con servicios web hospedados con el proveedor https://api.mejoratuciudad.org/, si vamos a esa URL podremos ver una bonita api REST muy bien documentada 🙂

rest

Esto es muy bueno ya que los desarrolladores ya no estamos limitados a solo utilizar la aplicación, ahora podemos crear nuestras propias aplicaciones utilizando la api ya existente :)! hay otros servicios web muy interesantes como:

Como mencionaba anteriormente, tal solo cambien el valor de la jurisdicción por alguno de los otros y obtendrán la información de las otras plataformas. Para obtener un poco mas de información general de la aplicación, ejecutamos fierce sobre mejoratuciudad.org para tratar de obtener una lista de posibles subdominios

fierce -dns mejoratuciudad.org -wordlist /root/tools/SecLists/Discovery/DNS/subdomains-top1mil-110000.txt -threads 10

fierce

La lista completa:

ftp.mejoratuciudad.org
www.mejoratuciudad.org
intranet.mejoratuciudad.org
api.mejoratuciudad.org
m.mejoratuciudad.org
apps.mejoratuciudad.org
blog.mejoratuciudad.org
demo.mejoratuciudad.org
lab.mejoratuciudad.org
doc.mejoratuciudad.org
developer.mejoratuciudad.org
pandora.mejoratuciudad.org
hera.mejoratuciudad.org
WWW.mejoratuciudad.org
landing.mejoratuciudad.org
privacy.mejoratuciudad.org
electra.mejoratuciudad.org
pi-canary.mejoratuciudad.org
api-developer.mejoratuciudad.org

En este punto el servidor me dio ban (probablemente por mis peticiones automatizadas, pero nada que no se solucione con una VPN), y ya no continué haciendo más pruebas, pero no sin antes darme cuenta de que al parecer su servidor no está hardenizado, cualquier petición a su api devolvía un error 403 revelando la versión del servidor web y su sistema operativo.

<html>
    <head>
        <title>403 Forbidden</title>
    </head>
    <body bgcolor="white">
        <center>
            <h1>403 Forbidden</h1>
        </center>
        <hr>
        <center>nginx/1.4.6 (Ubuntu)</center>
    </body>
</html>

Y si le corremos un scan con nmap a los puertos más conocidos obtenemos.

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.7 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http       nginx 1.4.6 (Ubuntu)
111/tcp  open  rpcbind    2-4 (RPC #100000)
443/tcp  open  ssl/http   nginx 1.4.6 (Ubuntu)
5666/tcp open  tcpwrapped
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Por buenas prácticas de seguridad esa información no debería ser revelada, y es bastante sencillo corregir eso (literalmente es modificar una línea en el archivo de configuración de nginx).

Con la ayuda de Burp Suite es posible analizar los request y eso nos arroja aún más información de cómo se comunica la app, aunque teniendo la documentación de su api a la mano ya no es tan necesario.

Para terminar, que es lo que aprendimos hoy

  • En el desarrollo de este proyecto estuvieron involucradas al menos 3 partes: El gobierno de Guadalajara, la empresa Radmas y Urbiotica (dejando de lado el posible caso de que en realidad ellos no tienen nada que ver y solo las credenciales de uno de sus clientes fueron leakeadas en la app, eso sería muy mala suerte)
  • Al menos la app de Android (que fue la que analice) fue desarrollada de forma nativa
  • Hacen buen uso de las funcionalidades para almacenar información, guardan la información sensible utilizando shared preference y bases de datos sqlite
  • El almacenamiento de imágenes lo hacen en la nube de aws
  • La app tiene credenciales hardcodeadas (posiblemente de un proyecto anterior del cual re utilizaron el código) y que al día de hoy funcionan
  • La app utiliza el servicio de mejoratuciudad.org que al parecer es un producto de radmas.com y podemos decir que es un software / plataforma / tecnologia especializada para hacer implementaciones en smart cities
  • Los servidores de mejoratuciudad.org no están hardenizados
  • Los servidores de mejoratuciudad.org cuentan con algún tipo de IDS/IPS que bloquea a usuarios después de X número de peticiones automatizadas.
  • La plataforma no implementa esos controles puesto que solo utilice una VPN para cambiar mi IP y ya pude seguir interactuando con la app sin problemas.
  • Debido al concepto de Jurisdicciones sabemos que la app está siendo implementada en muchas otras ciudades de México y el mundo (16 clientes para ser exactos según los servidores de mejoratuciudad.org)
  • Los servicios web no parecen vulnerables a algún tipo de ataque popular como SQL injection, xss, RCE, etc. Muy bien por Radmas 🙂 )
  • El código fuente de la aplicacion no fue ofuscado

Happy hacking … salu2

Los programadores y la seguridad informática

En esta ocasión quiero compartir con ustedes una pequeña experiencia que me sucedió en uno de los muchos grupos de programadores a los que pertenezco en la red social Facebook, esta anécdota me hizo reflexionar acerca del nivel de cultura de seguridad informática que tenemos en México y al final me hizo terminar escribiendo este post a manera de auto crítica constructiva para la misma comunidad de desarrolladores.

Son muchos y muy variados los grupos de programación que uno encuentra Facebook: desarrolladores web, desarrolladores iOS / Android, desarrolladores C#, desarrolladores de videojuegos y todo lo que uno se imagine; esta funcionalidad de Facebook (los grupos) ha pasado a sustituir los antiguos foros de discusión (como smf o phpbb) para la generación millennial ya que de alguna forma es mucho mas cómodo tener todo integrado en la misma plataforma social, pero ese no es el tema de discusión aquí.

La Anécdota

Lo que verdaderamente me llamo la atención fue hace unos días cuando un miembro del grupo hizo una consulta acerca de cómo podía realizar una validación de correo electrónico en un sistema que estaba desarrollando, no me llamo la atención la duda, pues todos los días hay gente que pregunta y mucha otra que le responde, lo que me llamo la atención fue la respuesta que le dio una persona del grupo y sobre todo la convicción con la que escribió la misma.

La imagen esta censurada por que la idea no es quemar a nadie sino más bien sacar algo bueno de todo esto.

La imagen esta censurada por que la idea no es quemar a nadie sino más bien sacar algo bueno de todo esto.

En cuanto termine de leer su respuesta no pude esperar para unirme a su conversación y explicarle por qué su solución era incorrecta desde el punto de vista de seguridad, lo bueno fue que no lo tomo mal y se interesó bastante en el tema.

La solución del comentario de Facebook va más o menos así

mail_fail

¿Pueden ver dónde está el problema de su solución?

La solución iba bien hasta el momento en donde decide regresar el token aleatorio al navegador del usuario y realizar ahí la validación. Explicando un poco más acerca de por qué este esquema de solución era malo le mostré un escenario en donde un atacante podría crear miles de cuentas saltando su sistema de verificación.

Haciendo un pequeño ejercicio de reflexión le propuse lo siguiente:

Partimos de la siguiente premisa: el objetivo del sistema de activación por correo es validar una identidad (así como detener posibles ataques automatizados que te creen cuentas falsas y te llenen la base de datos de usuarios ficticios), para lograr esto se utiliza el concepto de “secreto”, un secreto es un token que debe ser compartido solamente con el usuario mediante el método que él especifico (el correo electrónico), enviando ese token al correo que se supone tiene acceso y es el único medio en el que pueda recibir ese secreto nos aseguramos que la cuenta que el usuario especifico existe y es válida, por lo tanto su identidad ha sido avalada por un proveedor de correo.

Ahora, en el escenario que nos plantean de realizar toda la validación del lado del cliente el problema es que realmente no estas validando nada, si el servidor le está regresando al navegador mediante Ajax el token que se supone el usuario debe escribir ¿Cuál es el punto pedirle revisar su correo? Fácilmente se podría crear un bot que registrara cuentas y las activara de forma masiva, es por eso que no es una buena solución.

source-code-583537_1280

La solución óptima seria (o al menos como se usa en la industria de software hoy en día):

Al momento de crear el usuario en la base de datos debemos manejar un campo para representar el estado actual de su cuenta (activada o desactivada), y cuando el usuario reciba el token por correo este lo introduce en un formulario y es enviado para su comparación del lado del servidor (el token generado también debimos haberlo almacenado de alguna forma en el servidor) y dependiendo de si es válido o no activamos su cuenta.

Lo que me interesaba al final es dejarles en claro que todo el código y los datos (por ejemplo funciones de validación en JavaScript) que enviemos al navegador pueden ser modificados por el usuario, y por lo tanto jamás debemos confiar en esa información.

Bueno aquí termina la anécdota, satisfecho de saber que al menos ese programador siguiendo mis recomendaciones seria capas de lanzar ese componente de forma segura a producción me puse a pensar en todos los demás desarrolladores que tienen dudas pero por X o Y motivo no preguntan y siguen produciendo mas y mas código vulnerable que al final contribuye a llenar las bases de datos sobre hackeos de sitios como https://haveibeenpwned.com o http://www.zone-h.org/

Mi opinión

Como muchos sabrán, actualmente soy consultor de seguridad informática, sin embargo tengo una formación de desarrollador de software, antes de dedicarme a la seguridad y durante la universidad cree muchísimas plataformas web y aplicaciones móviles (y lo sigo haciendo de vez en cuando como FreeLancer xd), estoy convencido de que al realizar una prueba de penetración en algún sistema o aplicación, en el 95% de los casos los hackeos se hubieran evitado si el software hubiera sido diseñado con la seguridad en mente.

Desarrollar software seguro no es una tarea sencilla, nadie nunca lo dijo, pero las empresas de desarrollo y pequeñas agencias tienen la manera de mitigar este problema.

Capacitación del personal

En mi opinión por cada equipo de desarrolladores debería de haber por lo menos una persona con nociones básicas de seguridad, “Nociones básicas” es un término muy relativo, es por eso que las empresas (lideres técnicos, Project managers, IT managers, etc.) deberían voltear a ver estándares y buenas prácticas de seguridad en el desarrollo de software y no solo eso, integrarlas en su ciclo de desarrollo.

Hay un documento que me gusta recomendar bastante.

OWASP Top 10: los diez riesgos más críticos en aplicaciones web

Este documento explica las diez vulnerabilidades más populares en las aplicaciones web hoy en día, así como su riesgo, impacto y mitigación.

owasp

Ya sea que trabajes para una empresa o seas un desarrollador FreeLancer, te aseguro que si desarrollas tu software tomando como referencia los 10 puntos de OWASP tu aplicación sera lo suficientemente robusta para que no venga cualquier lammer / hackerucho de tercera y vulnere tu sistema usando sus herramientas automatizadas.

Como decía mas arriba, producir software seguro es compromiso de todos, las metodologías de desarrollo ágil como SCRUM son buenas y funcionan, pero muchas veces el personal que las lleva acabo deja de lado el tiempo requerido para hacer un buen diseño de software (con la seguridad en mente, programación defensiva, etc.) o lo minimiza

Aunque lo ideal sería tener un departamento de seguridad que apoyara al departamento de desarrollo durante todo el ciclo de vida del proyecto.

Después de todo, ¿cuánto te cuesta contratar un arquitecto de seguridad? ¿Cuánto te costaría recuperarte de un hackeo (hablando de información, dinero y prestigio de la empresa)?

Tutorial Material Design en Android #5

Crear un RecyclerView para Android

Hola lectores, ha pasado un tiempo desde que publique la última entrega del tutorial de Material Design en Android en donde aprendimos a crear un Navigation Drawer, continuamos con los tutoriales y en esta ocasión les enseñare a crear un RecyclerView.

Pero antes, ¿Que es un RecyclerView?, seguramente muchos lo abran visto ya en acción, este tipo de componente es cada vez más y más popular y ha venido remplazando a las antiguas ListViews, se trata de un componente que nos permite crear listas de contenedores que a su vez pueden tener dentro otros componentes (TextView, EditText, ImageView, etc.). El RecyclerView tiene la particularidad de haber sido diseñado con la eficiencia en mente, como su nombre lo indica los objetos que son visible en la pantalla son los que se dibujan en pantalla y una vez que desaparecen (el usuario hace scroll) estos remplazan su contenido (se reciclan) para mostrar otro tipo de información.

Imaginemos una lista de 100 contactos, en la pantalla de nuestro teléfono solo podemos renderizar 5 contactos a la vez, en lugar de renderizar los 100 desde un inicio solo vamos remplazando la información en las filas de nuestro RecyclerView conforme lo vamos necesitando, cabe decir que este es un proceso automático.

list_mail

Pero el RecyclerView es mucho más que eso, a diferencia de su predecesor con este nuevo componente podemos crear listas con diferentes tipos de layouts

20150415193645985

images

EAF-MD3

Retomamos nuestro proyecto, como les comentaba en la publicación pasada he subido el código realizado hasta el momento en el siguiente repositorio https://github.com/Alevsk/Material-design-en-Android.

Lo primero que vamos a hacer sera agregar la dependencia a nuestro proyecto en el archivo de gradle, abrimos el archivo build.gradle que se encuentra en MaterialdesignApp (proyecto)> app > build.gradle y en la parte de abajo en el apartado de dependencias agregaremos
compile ‘com.android.support:recyclerview-v7:+’ quedando de la siguiente forma

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile 'com.android.support:recyclerview-v7:22.2.0'
}

Sincronizamos gradle para descargar las dependencias (o también podemos dar re build al proyecto) y una vez tenemos la dependencia vamos a comenzar a utilizar el componente, vamos a nuestro archivo fragment_navigation_drawer.xml (aquí vamos a crear el menú), si no recuerdan donde está el archivo se encuentra en MaterialdesignApp > app > src > main > res > layout > fragment_navigation_drawer.xml

Por el momento tenemos esto, un simple texto ¿bastante simple cierto?

Capture

drawer_navigation

Al final de nuestro tutorial vamos aprender a crear algo como esto :), pues manos a la obra, o mejor dicho al codigo XD

16170261501_9b7ce86ca7_b

En nuestro archivo XML (fragment_navigation_drawer.xml) vamos a crear nuestro RecyclerView con el siguiente código, si se fijan elimine el TextView que teníamos porque ya no lo necesitamos.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/grayPanel"
    tools:context="com.alevsk.materialdesignapp.fragments.NavigationDrawerFragment">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/drawerList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

Una vez añadido el componente en nuestra vista xml vamos a nuestro controller NavigationDrawerFragment.java (MaterialdesignApp > app > src > main > java > com.alevsk.materialdesignapp > fragments > NavigationDrawerFragment.java) y definimos una nueva variable tipo RecyclerView como propiedad de la clase

public class NavigationDrawerFragment extends Fragment {

    private RecyclerView mRecyclerView;
    private ActionBarDrawerToggle mDrawerToggle;
    private DrawerLayout mDrawerLayout;
    private Toolbar mToolbar;
    ....
    ..

Una vez creado nuestro RecyclerView vamos a agregar la referencia hacia su objeto xml, para eso en el método onCreateView, vamos a modificar un poco el código, actualmente tenemos algo como esto

Capture

Lo remplazamos por esto, sencillo, a estas alturas del tutorial ya deberían saber que estamos obteniendo el elemento mediante el id (layout.findViewById(R.id.drawerList)) que definimos, estamos haciendo un cast (RecyclerView) y lo estamos asignando a la variable mRecyclerView, por ultimo retornamos el layout (View) que es con el que trabajara la aplicacion.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View layout = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
        mRecyclerView = (RecyclerView) layout.findViewById(R.id.drawerList);
        return layout;
    }

Pero no tan rápido, antes de correr nuestra aplicación nos falta crear un par de componentes más, un Adapter (un manejador lógico para los elementos de nuestra lista), ViewHolders y un modelo (representación de un elemento en la lista)

Pero antes, para tener todo más organizado, vamos a crear dos nuevos Package en nuestro proyecto, uno llamado adapters y otro models

Capture

Vamos a comenzar a pensar en MVC (Model View Controller), si bien no existe un estándar totalmente definido para utilizar MVC en Android podemos tomar el concepto y definir lo siguiente:

  • Model: Qué vamos a renderizar en la aplicación (objetos principales utilizados en los controladores)
  • View: Como lo vamos a renderizar (xml)
  • Controller: Eventos del ciclo de vida de las pantallas, manejar las entradas de usuario, definición de componentes de Android como el RecyclerView, etc.

Creando un Modelo en Android

Teniendo lo anterior como premisa al momento de definir nuestro menú lo que necesitamos es una imagen que sirva como icono y un texto que sirva como título, procedemos a crear nuestra clase Menu.java en el Package models que definimos previamente

Quicktip: en nuestra clase primero definimos las propiedades de nuestro objeto (int icon, String title) y después para generar nuestros gets y sets usamos el shortcut “alt + insert” lo que desplegara un menú en donde elegimos getters and settters y marcamos todas las propiedades (variables) de nuestra clase.

Capture

public class Menu {
    private int icon;
    private String title;

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Creando un Adapter personalizado para el RecyclerView

Ahora en el Package adapters creamos una clase llamada MenuAdapter que extenderá o heredara sus metodos de RecyclerView.Adapter, el código inicialmente queda así.

package com.alevsk.materialdesignapp.adapters;

import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

/**
 * Created by Alevskey on 23/01/2016.
 */
public class MenuAdapter extends RecyclerView.Adapter {
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }
}

Creando un ViewHolder personalizado

Ademas de eso también vamos a crear un ViewHolder, podemos definir la clase como una subclase de nuestro MenuAdapter, como siempre aquí tienen más documentación respecto a lo que vamos a utilizar http://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html

Quedando el codigo final de la siguiente forma

Capture

Continuamos, Si leyeron la documentación acerca del comportamiento de los Adapter sabrán que por default manejan el tipo de objeto ViewHolder, pero nosotros acabamos de definir uno llamado MenuViewHolder que será el encargado de “contener” elementos como TextView, ImageView y todo lo que necesitemos para personalizar nuestro elemento de menú, sabiendo eso necesitamos realizar algunos cambios en nuestra clase, para empezar en la definición de nuestra clase vamos a definir (valga la redundancia) explícitamente que queremos utilizar MenuViewHolder

public class MenuAdapter extends RecyclerView.Adapter<MenuAdapter.MenuViewHolder> {
...
..
.

Ahora tenemos un montón de errores en la clase y esto es porque tenemos que modificar el tipo de objeto que retorna la función onCreateViewHolder de RecyclerView.ViewHolder a MenuViewHolder, también deberemos definir otra función llamada OnBindViewHolder, una vez realizados los cambios tenemos lo siguiente.

public class MenuAdapter extends RecyclerView.Adapter<MenuAdapter.MenuViewHolder> {
    @Override
    public MenuViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(MenuViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }

    class MenuViewHolder extends RecyclerView.ViewHolder {

        public MenuViewHolder(View itemView) {
            super(itemView);
        }
    }
}

Ahora vamos a crear la vista xml de nuestro item de Menu, en la carpeta layout creamos un archivo llamado viewholder_menu.xml que contendrá dos cosas, un ImageView y un TextView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/listIcon"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:padding="8dp"
        android:layout_gravity="center_vertical"
        android:src="@drawable/dummy" />

    <TextView
        android:id="@+id/listText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:layout_gravity="center_vertical"
        android:text="@string/dummy_text" />

</LinearLayout>

La imagen @drawable/dummy es una imagen de ejemplo que yo utilice, ustedes pueden usar la que más les guste.

Regresamos a nuestro MenuAdapter.java y vamos a comenzar a utilizar los métodos que creamos anteriormente, primero vamos a crear un constructor para nuestro adapter, el cual recibirá un Context que nos servirá para hacer el binding de los elementos xml en nuestro ViewHolder, y una lista (de objetos Menu) que será la información con la cual llenaremos el RecyclerView después de eso vamos a realizar ese binding en el método onCreateView, tambien modificamos la función getItemCount para retornar el tamaño actual de la lista.

    private LayoutInflater mInflater;
    private List<Menu> data = Collections.emptyList();

    public MenuAdapter(Context context, List<Menu> data) {
        mInflater = LayoutInflater.from(context);
        this.data = data;
    }

    @Override
    public MenuViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.viewholder_menu, parent, false);
        MenuViewHolder holder = new MenuViewHolder(view);
        return holder;
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

Ahora en la subclase MenuViewHolder vamos a definir el ImageView y TextView

    class MenuViewHolder extends RecyclerView.ViewHolder {
        ImageView icon;
        TextView title;
        public MenuViewHolder(View itemView) {
            super(itemView);
            title = (TextView) itemView.findViewById(R.id.listText);
            icon = (ImageView) itemView.findViewById(R.id.listIcon);
        }
    }

Ahora en el método onBindViewHolder es donde se realiza la acción de asignar los valores a las variables del MenuHolder (imágenes, textos, etc).

    public void onBindViewHolder(MenuViewHolder holder, int position) {
        holder.title.setText(data.get(position).getTitle());
        holder.icon.setImageResource(data.get(position).getIcon());
    }

Lo que hace nuestro método es recibir un holder, y una posición int, entonces con esa información de nuestra Lista de objetos Menu extraemos el titulo y el id de recurso de imagen que contiene el objeto Menu en la posición indicada de la lista, por ultimo hacemos set de esa información al TextView e ImageView del holder.

Venga ya falta poco, casi terminamos!!!

Me di cuenta que aún no hemos definido el tema de la app, lo he estado pensando y me gustaría enfocarla a un pequeño catálogo de anime que consuma la api opensource de hummingbird, desarrollando un catálogo les poder enseñar a utilizar muchos más componentes que el ecosistema de Android nos ofrece, así como a consumir Apis e interactuar con las mismas, algo muy común en la industria de desarrollo de aplicaciones hoy en día.

Teniendo claro lo anterior las opciones de nuestro menú serán:

  • Buscar (buscador de anime)
  • Lo que estoy viendo (anime que el usuario ve actualmente)
  • Para más tarde (anime que el usuario tiene en la categoría de plan-to-watch)
  • Completado (series que el usuario ha terminado de ver)
  • Mi cuenta (información del usuario y anime favorito)

De ahí hay 3 opciones que solo pueden ser utilizados una vez el usuario haya sido autenticado en nuestra aplicación, pero eso lo dejaremos para más tarde, por ahora nos centraremos en desarrollar el menú.

Primero vamos a nuestro archivo strings.xml que se encuentra en MaterialdesignApp > app > src > main > res > values > strings.xml y ahi vamos a crear una lista de items que contendrá los elementos del menú

    <string-array name="menu_list">
        <item>Buscar</item>
        <item>Lo que estoy viendo</item>
        <item>Para mas tarde</item>
        <item>Mi cuenta</item>
    </string-array>

Buscamos en Internet algún set de iconos gratuitos y los incluimos a los recursos de nuestro proyecto en la carpeta res > drawable pues los necesitamos para el menu

    <array name="menu_icons">
        <item>@drawable/ic_search</item>
        <item>@drawable/ic_eye</item>
        <item>@drawable/ic_ticket</item>
        <item>@drawable/ic_account</item>
    </array>

Cargando los datos al RecyclerView

En nuestro archivo NavigationDrawerFragment.java vamos a crear una nueva función llamada getData(), esta función será la encargada de leer la información estática que definimos en nuestro archivo strings.xml

    public List<Menu> getData() {
        List<Menu> menu = new ArrayList<Menu>();
        TypedArray icons = getResources().obtainTypedArray(R.array.menu_icons);
        String[] labels = getResources().getStringArray(R.array.menu_list);

        for(int i = 0; i < labels.length; i++) {
            Menu item = new Menu();
            item.setTitle(labels[i]);
            item.setIcon(icons.getResourceId(i,-1));
            menu.add(item);
        }

        return menu;
    }

Lo que estamos haciendo en nuestra función es leer los títulos e imágenes (ids de recurso) y con ello estamos instanciando objetos Menu que a su vez agregamos a una lista que será devuelta por el método.

Finalmente en nuestra función onCreateView vamos a crear una nueva instancia de nuestro Adapter personalizado y se lo vamos a pasar al RecyclerView, también definiremos un objeto llamado LayoutManager para nuestro RecyclerView (asegúrate de definir el Adapter como una propiedad de tu clase)

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View layout = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
        mRecyclerView = (RecyclerView) layout.findViewById(R.id.drawerList);
        mAdapter = new MenuAdapter(getActivity(),getData());
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        return layout;
    }

Corremos la aplicación en nuestro emulador o teléfono físico, abrimos el NavigationDrawer y vemos nuestro menú 🙂

recyclerview

Eso es todo por ahora lectores, en el siguiente tutorial aprenderemos a aplicar color a los iconos utilizando filtros y a definir las acciones una vez que el usuario da Tap en un elemento del menú. Recuerden que el código de lo trabajado durante este tutorial lo pueden encontrar en el repositorio https://github.com/Alevsk/Material-design-en-Android
salu2

Mi experiencia durante el I/O Hack 2015

Hola lectores, tengo varios post acumulados que me gustaría publicar antes de que termine el año, varios son nuevos tutoriales de desarrollo, avances en mi investigación sobre la seguridad en SmartTvs y eventos de tecnología a los que he asistido estos últimos meses.

El 13 y 14 de noviembre se llevó acabo el I/O Hack 2015 en el Tecnológico de Monterrey campus Santa fe y tuve la fortuna de ser uno de los 40 seleccionados para asistir al hackaton. Era la primera vez que asistía a un evento de este tipo, ya sabía de la existencia de los hackatones y sabia más o menos bien de que se trataba pero nunca había tenido la oportunidad de participar en uno.

Pues llegue el viernes 13 (nunca he creído en la mala suerte hehe) al evento sin conocer a nadie, los organizadores y todo el staff eran bastante amable y procuraban que uno se sintiera cómodo en todo momento, ya una vez dentro de las instalaciones se respiraba un aire muy parecido al de un evento en campus party, pequeños grupos hablando sobre tecnología, personas presentándose y hablando un poco acerca de lo que hacen, un par de impresoras 3D en el centro haciendo modelos, etc. rápidamente me integre a uno de los grupos, me presente y comenzamos a platicar acerca de que les parecía el evento, en que programaban, que frameworks manejaban, hardware y demás cosas en lo que empezaba oficialmente el evento.

El hackaton tenía como temática el Internet de las cosas y había 3 categorías principales en las que se podía participar, estas eran:

  • Agricultura
  • Logistica
  • Retail (soluciones para pymes)

Por cada una de ellas se nos dio una plática y se nos hablo acerca de problemáticas muy puntuales a las que se enfrentan hoy en día, en lo personal me gusto bastante la charla sobre agricultura ya que representa una industria millonaria en México, y uno pensaría que agricultura y tecnología no tienen nada en común pero la verdad es que muchos de sus problemas si se pueden resolver usando tecnología.

Para no extenderme demasiado solo diré que al inicio del evento conocí dos personas bastante interesantes, quienes ademas de tener solidas habilidades en programación eran bastante buenos en las cuestiones de algoritmos, complejidad computacional y específicamente tratamiento de imágenes, Fernando Hernandez y Marco Antonio Cruz, al final participamos en la categoría de agricultura desarrollando una solución de reconocimiento de imágenes tomadas mediante fotografía aérea con drones, la idea era detectar cuando una plantación de caña de azúcar comenzara a secarse (analizábamos los niveles de amarillo en las puntas de la planta desde arriba) y con eso alimentábamos una red neuronal para determinar si se estaba echando a perder el producto, desarrollamos nuestro prototipo, hicimos las pruebas y lo presentamos a los jueces obteniendo el tercer lugar del Hackaton 🙂

Bueno hasta aquí mi resumen sobre el evento, lo que más me gusto y con lo que me quede fueron las experiencias que viví, toda la gente que conocí, las tecnologías que aprendí y sobre todo los buenos amigos que hice, ahora, mi intención con este post además de contarles un poco lo que viví es invitarlos a participar en un evento de este tipo siempre que puedan, así que les dejo los pros y contras de un Hackaton.

Pros de asistir a un Hackaton

  • Conoces gente muy interesante (TI, Makers, hackers, etc)
  • Te expones a nuevas tecnologías
  • Aprendes lo que sabes y te enseñan nuevas cosas
  • Haces amigos
  • Resuelves retos y problemas interesantes

Contras de asistir a un Hackaton

  • Si el evento no ofrece viáticos a sus participantes pues es un gasto
  • Si no te gusta desvelarte frente a una computadora eso es un problema
  • Si eres celoso al compartir tus ideas eso es un problema

Les dejo algunas fotos del evento:

CTxZiYVUEAEj3x0

CTytigUUAAAoUmn

CTvbH3WUwAEVMuy

CTu0T0rUYAA0sXc

12243283_10206406595418557_2330651871028124945_n