14 septiembre, 2015 Alberto

Diseño de una alarma antirrobo en Python con Ycar

Ejemplo del coche fantástico

Ahora que estoy en Barcelona dejo el coche durante largos periodos de tiempo en la calle y no me dejan de preguntar si no tengo miedo a que «me lo roben».  Mi coche es del año 1997, a punto de convertirse en un clásico, y si me lo robaran lejos de importarme creo que me ayudarían a decidirme de una vez por todas a cambiarlo.  A raíz de estas preguntas se me ocurrió cómo mi coche tan antiguo podía pedirme auxilio en caso de que fuera tomado prestado por algún amigo de lo ajeno.

Desde que tengo carnet de conducir, hace más de diez años, soy cliente de la aseguradora Mapfre y desde el año 2011 me ofrecieron contratar la póliza de seguros para gente «joven» Ycar.  Con esta póliza aceptas la instalación «ocultad» de un dispositivo GPS  y que a través de GPRS actualiza cada 2.000 metros la posición actual de tu vehículo.  Además dispone de un acelerómetro como el de los smartphones para que en caso de accidente se avise a la compañía aseguradora.  En caso de aviso la aseguradora te llama por teléfono y si no respondes a la llamada avisan a los servicios de emergencia.  El objetivo principal por parte de Mapfre es obtener estadísticas de la conducción de sus clientes y desde el punto de vista de los clientes es la obtención de descuentos en la renovación de la póliza en función del número de Kms recorridos y el cumplimiento de la velocidad máxima permitida.  Para cumplir con la Ley Orgánica de Protección de Datos la gestión de esta información se realiza a través de una tercera empresa y Mapfre sólo puede conocer la posición de tu vehículo a menos que tú denuncies su robo.  En cambio tú puedes ver la posición en todo momento a través de la aplicación para dispositivos móviles Ycar.  En teoría la información que recibe Mapfre está anonimizada.

"Jóvenes" Ycar

«Jóvenes» Ycar

Con semejante despliegue tecnológico en mi coche de la era analógica se me ocurrió diseñar el siguiente sistema de alarma antirobo.

He desarrollado un programa que se conecta periódicamente (en este caso cada 15 minutos) a la página web de Mapfre con mis credenciales y consulta la posición del coche, si la posición ha variado envía una alerta al teléfono móvil a través de un correo electrónico.  ¿Qué ocurre si él que ha movido el coche soy yo?  Pues con el bot para telegram que expliqué en la última entrada puedo activar o desactivar la alarma desde mi teléfono móvil.  Así que cuando estoy por Barcelona intentándome ganara la vida activo la alarma y cuando vuelvo la desactivo.

Ahora detallo la parte técnica (cuando la mayoría de la gente deja de leer).  El programa que se conecta periódicamente a la web de Mapfre es un script en Python que utiliza la librería Scrapy para crawlear la web (araña web).  Aquí el código fuente:


# -*- coding: utf-8 -*-
from scrapy.contrib.spiders.init import InitSpider
from scrapy.http import Request, FormRequest
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.contrib.spiders import Rule 
import smtplib
import sys
import time

with open("/Users/Documents/YCarLog.txt", "w") as f:
    f.write("[+] YCarWeb ejecutado a las " + time.ctime())

with open('/Users/Documents/EstadoAlarma.txt') as f:
        content = f.readlines()
        print '[+] Estado de la alarma: ' + str(content[0])
        if content[0] == "OFF":
            print '[+] Salir'
            sys.exit(0)


class ycar(InitSpider):
    name = 'ycar'
    allowed_domains = ['mapfre.es']
    login_page = 'https://www.mapfre.es/oim/ValidarIdentificacionAction.do'
    start_urls = ['https://www.mapfre.es/oim/DetalleProductoMutuaActionTR.do?destino=subhome&numContratoActual=micontrato&entidad=01']


    def init_request(self):
        """This function is called before crawling starts."""
        return Request(url=self.login_page, callback=self.login)

    def login(self, response):
        """Generate a login request."""
        return FormRequest.from_response(response,
                    formdata={'txtUsuario': 'miusuario', 'txtClave': 'miclave'},
                    callback=self.check_login_response)

    def check_login_response(self, response):
        """Check the response returned by a login request to see if we are
        successfully logged in.
        """
        #print '[+] response' + response.body
        if "MAGALLON" in response.body:
            self.log("Successfully logged in. Let's start crawling!")
            # Now the crawling can begin..
            return self.initialized()
        else:
            self.log("Bad times :(")
            # Something went wrong, we couldn't log in, so nothing happens.

    def parse(self, response):

        print '====================================================================='

        if "Por favor, vuelva a intentarlo pasado unos minutos, si el" in response.body:
            self.log("[+] Erro de la web de Mapfre")
        else:
            km = response.xpath('//body[@class="home pestTC logado"]/section[@id="contenido"]/div[@id="contenidoInt"]/div[@class="interior twoCols cl"]/section[@id="principal"]/form[@class="caja"]/article[@class="C70 boxshadow roundcorners"][4]/div[@class="info"]/p/span/a/text()').extract()
            print '[+] km ' + str(km)
            print "[+] Comprobar km"
            fh = open("/Users/Documents/ycarKms.txt","a")
            fh.write('\n' + str(km))
            fh.close()

            fh = open("/Users/Documents/ycarKms.txt","r")
            content = fh.readlines()
            print '[+] content[0]: ' + str(content[0])
            print '[+] content[1]: ' + str(content[1])
            fh.close()

            if content[0].rstrip('\n') == content[1].rstrip('\n'):
                print "[+] No hay alarma"
                fh = open("/Users/Documents/ycarKms.txt","r")
                lines = fh.readlines()
                fh.close()
                fh = open("/Users/Documents/ycarKms.txt","w")
                for line in lines:
                    if line!=str(content[0]):
                        fh.write(line)
                fh.close()
            else:
                fh = open("/Users/Documents/ycarKms.txt","r")
                lines = fh.readlines()
                fh.close()
                fh = open("/Users/Documents/ycarKms.txt","w")
                for line in lines:
                    print '[+] line:' + line
                    print '[+] content[0]:' + str(content[0])
                    if line!=content[0]:
                        fh.write(line)
                        print '[+] Escribe' + line
                    else:
                        print "[+] Linea encontrada"
                fh.close()
                print "[+] Alarma!"
                fromaddr = 'albertomagallonsabado@gmail.com'
                toaddrs  = 'albertomagallonsabado@gmail.com'
                subject = 'Alarma antirobo python'
                text = '¡Alarma! El coche fantástico en movimiento.'
                msg = 'Subject: %s\n\n%s' % (subject, text)
                username = 'micorreo@gmail.com'
                password = 'micontrasena'
                server = smtplib.SMTP('smtp.gmail.com:587')
                server.ehlo()
                server.starttls()
                server.login(username,password)
                server.sendmail(fromaddr, toaddrs, msg)
                server.quit()

El código es totalmente optimizable pero tampoco he querido perder más tiempo en este proyecto.

En este código se tratan varios conceptos que no había tenido ocasión de comentar todavía en el blog.  Por un lado esta la utilización de scrapy para crear arañas web y por otro lado está la utilización de rutas Xpath para acceder a recursos web.  El código fuente anterior es un buen ejemplo de los dos conceptos.

Esta es la salida del script:

root@kali:~/aranas/Ycar# scrapy crawl ycar
/root/aranas/Ycar/Ycar/spiders/ycar.py:1: ScrapyDeprecationWarning: Module `scrapy.contrib.spiders` is deprecated, use `scrapy.spiders` instead
  from scrapy.contrib.spiders.init import InitSpider
/root/aranas/Ycar/Ycar/spiders/ycar.py:1: ScrapyDeprecationWarning: Module `scrapy.contrib.spiders.init` is deprecated, use `scrapy.spiders.init` instead
  from scrapy.contrib.spiders.init import InitSpider
/root/aranas/Ycar/Ycar/spiders/ycar.py:3: ScrapyDeprecationWarning: Module `scrapy.contrib.linkextractors` is deprecated, use `scrapy.linkextractors` instead
  from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
/root/aranas/Ycar/Ycar/spiders/ycar.py:3: ScrapyDeprecationWarning: Module `scrapy.contrib.linkextractors.sgml` is deprecated, use `scrapy.linkextractors.sgml` instead
  from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
[+] Estado de la alarma: ON
2015-09-12 16:04:43 [scrapy] INFO: Scrapy 1.0.3 started (bot: Ycar)
2015-09-12 16:04:43 [scrapy] INFO: Optional features available: ssl, http11
2015-09-12 16:04:43 [scrapy] INFO: Overridden settings: {'NEWSPIDER_MODULE': 'Ycar.spiders', 'SPIDER_MODULES': ['Ycar.spiders'], 'BOT_NAME': 'Ycar'}
2015-09-12 16:04:47 [scrapy] INFO: Enabled extensions: CloseSpider, TelnetConsole, LogStats, CoreStats, SpiderState
2015-09-12 16:04:49 [scrapy] INFO: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats
2015-09-12 16:04:49 [scrapy] INFO: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware
2015-09-12 16:04:49 [scrapy] INFO: Enabled item pipelines: 
2015-09-12 16:04:49 [scrapy] INFO: Spider opened
2015-09-12 16:04:49 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2015-09-12 16:04:49 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2015-09-12 16:04:55 [scrapy] DEBUG: Crawled (200) <GET https://www.mapfre.es/oim/ValidarIdentificacionAction.do> (referer: None)
2015-09-12 16:04:55 [scrapy] DEBUG: Redirecting (302) to <GET https://www.mapfre.es/oim/ComprobarIrDirectoAction.do> from <POST https://www.mapfre.es/oim/ValidarUsuarioClaveAction.do>
2015-09-12 16:05:05 [scrapy] DEBUG: Crawled (200) <GET https://www.mapfre.es/oim/ComprobarIrDirectoAction.do> (referer: https://www.mapfre.es/oim/ValidarIdentificacionAction.do)
2015-09-12 16:05:05 [ycar] DEBUG: [+] Auntenticado en Mapfre continua el crawling
2015-09-12 16:05:12 [scrapy] DEBUG: Crawled (200) <GET https://www.mapfre.es/oim/DetalleProductoMutuaActionTR.do?destino=subhome&numContratoActual=NNNNNNNNNN&entidad=01> (referer: https://www.mapfre.es/oim/ComprobarIrDirectoAction.do)
=====================================================================
[+] km [u'4.836 Km']
[+] Comprobar km
[+] content[0]: [u'4.836 Km']

[+] content[1]: [u'4.836 Km']
[+] No hay alarma
2015-09-12 16:05:12 [scrapy] INFO: Closing spider (finished)
2015-09-12 16:05:12 [scrapy] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 1612,
 'downloader/request_count': 4,
 'downloader/request_method_count/GET': 3,
 'downloader/request_method_count/POST': 1,
 'downloader/response_bytes': 68484,
 'downloader/response_count': 4,
 'downloader/response_status_count/200': 3,
 'downloader/response_status_count/302': 1,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2015, 9, 12, 14, 5, 12, 892269),
 'log_count/DEBUG': 6,
 'log_count/INFO': 7,
 'request_depth_max': 2,
 'response_received_count': 3,
 'scheduler/dequeued': 4,
 'scheduler/dequeued/memory': 4,
 'scheduler/enqueued': 4,
 'scheduler/enqueued/memory': 4,
 'start_time': datetime.datetime(2015, 9, 12, 14, 4, 49, 549488)}
2015-09-12 16:05:12 [scrapy] INFO: Spider closed (finished)

Una vez que tenemos el script operativo sólo hay que incluirlo en el programador de tareas para que se ejecute periódicamente.  Para ello lo he incluído en mi Raspberry Pi que hace la labor de 24×7.

*/15 * * * * cd /root/aranas/Ycar/ && /usr/local/bin/scrapy crawl ycar >/dev/null 2>&1

El resultado en caso de alerta es un mensaje como el siguiente:

Alerta antirobo email

Alerta antirrobo email

También se podría pedir al bot de Telegram que fuese él quien nos alertase.  Las posibilidades son muchas.  Mapfre podría dar como servicio a sus clientes un sistema de alertas antirrobo similar, en vez de activar o desactivar la alarma a través de un bot de Telegram se podría integrar con bluetooth para que cuando te montes en el coche se desactive la alarma.

Durante la realización de este proyecto encontré algunas brechas de seguridad en la aplicación para móvil Ycar que trataré de explicar más adelante.

 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

ACEPTAR
Aviso de cookies