En mi artículo anterior, pude demostrar un contenedor Kali Linux ejecutándose con Tor Browser y conectado a su entorno de escritorio con un cliente VNC. Verifiqué que Tor Browser se estaba conectando a Tor Network durante una sesión de navegación. Esta configuración me permitirá simular el tipo de tráfico que podría provenir de un atacante que apunta a un sitio web.
En este experimento, usaré Selenium para automatizar el Navegador Tor para sintetizar pulsaciones de teclas y eventos de navegación a través de la interfaz WebDriver del navegador. Cada rastreador tendrá una dirección IP aleatoria proporcionada por el Tor Proxy integrado para evadir la detección. Después de guardar los resultados como objetos JSON en el sistema de archivos local, usaré Python para procesarlos en un solo archivo CSV. Finalmente, discutiré qué contramedidas se pueden aplicar en un centro de datos y en el lado del cliente para intentar detectar, limitar la tasa y bloquear la actividad de los bots.
Todos los archivos y las licencias correspondientes están disponibles en este repositorio de código abierto: tor-driver-python
Tengo experiencia en automatización de pruebas y he pasado muchas horas diseñando pruebas. También pasé mucho tiempo trabajando con Selenium, y lo he usado en muchos lenguajes de programación y configuraciones diferentes para automatizar los navegadores web con fines de prueba. Hay escenarios en los que solo es posible probar una aplicación web con un navegador real, y Selenium es una gran herramienta para eso.
En mi trabajo como ingeniero de DevOps, he pasado una gran cantidad de tiempo preocupándome por qué hacer con los rastreadores web que atacan, y en ocasiones atacan directamente, las aplicaciones web de las que soy responsable. Pensé que sería un experimento interesante explorar el otro lado de este problema por una vez.
Quiero ver qué tan cerca puedo llegar a simular un ataque de una red de bots con fines educativos y analizar métodos para contrarrestar cosas como el tráfico sospechoso de la red Tor en un centro de datos moderno. Las botnets se usan comúnmente para realizar ataques de Credential Stuffing. Usaré una técnica similar para buscar consultas y recopilar información de la web.
El relleno de credenciales es la inyección automatizada de pares de nombre de usuario y contraseña robados ("credenciales") en los formularios de inicio de sesión del sitio web, para obtener acceso fraudulento a las cuentas de los usuarios. 1
Para evitar problemas éticos, al mismo tiempo que intenta mantenerse fiel a la tarea. Estoy haciendo los siguientes cambios en el escenario:
robots.txt
permisivos, y los Términos y condiciones se verificaron en el momento de la redacción para que no excluyeran el rastreo. Por ejemplo , los Términos y condiciones de IMDB prohíben explícitamente el rastreo sin consentimiento por escrito.
El Protocolo de exclusión de robots es una forma en que los webmasters les dicen a los rastreadores dónde están y dónde no pueden recopilar información. Puede encontrar más información y ejemplos en el sitio web robotstxt.org . Encontré un artículo: Lista de motores de búsqueda alternativos mientras intentaba encontrar uno que permitiera el web scraping en la página de resultados de búsqueda. A continuación se muestra un resumen de esa investigación.
Buscador | URL de robots.txt | ¿Está permitido gatear? |
---|---|---|
No, pero tiene una Api | ||
No, pero tiene una Api | ||
No | ||
No, pero tiene una Api | ||
Sí, pero no exactamente lo que estaba buscando. | ||
Sí |
Algunos otros recursos que encontré útiles mientras investigaba este tema:
Voy a evitar usar bibliotecas que no sean Selenium para este ejemplo. Hay algunos patrones realmente básicos que quiero demostrar y no quiero empantanarme con un lenguaje específico de dominio (DSL) en particular que podría dificultar la comprensión de lo que está sucediendo.
Sin embargo, creo que usar un marco de ejecución de prueba es una excelente manera de organizar este tipo de código. Agregar un marco puede resolver muchos problemas relacionados con la estructura general del código, la lógica de reintento e incluso los informes.
Hay un patrón básico sobre cómo manejo una página en una sesión de WebDriver. También agrego una pausa después de cada acción realizada. La automatización del navegador puede ser escamosa. Los tiempos de espera agregan mucha estabilidad al rastreo y limitan en gran medida las posibilidades de que la velocidad se limite y se bloquee. Siempre que sea necesario, también aumento el rastreo con llamadas API a otros motores de búsqueda o fuentes de información.
Tomé un enfoque realmente simple para los selectores. Estoy usando los selectores xpath y css que están disponibles en el navegador. Centrándose principalmente en etiquetas de anclaje y fragmentos de URL para navegar entre páginas durante un rastreo.
Estoy usando las condiciones esperadas para esperar a que los elementos estén presentes antes de intentar hacer clic en ellos. El proyecto Selenium tiene mucha documentación, pero también encontré que la discusión sobre las condiciones de espera con usos de ejemplo en Stack Overflow es un recurso invaluable.
Existe un proyecto PyPi existente llamado tbselenium que tiene una función similar. Para este experimento, hice referencia a la configuración del perfil de Firefox, pero no necesité ninguna de las otras características que incluye tbselenium. La complejidad adicional de los contenedores que no tienen acceso raíz contribuía a dificultar la depuración. Esto se sumó a la motivación para limitar las dependencias y probar soluciones simples preexistentes. Por ejemplo, hay muchos lugares en los que uso herramientas y subcapas de Linux en lugar de implementar soluciones de Python puras directamente.
La clase terminada tiene aproximadamente 150 líneas de Python. Creo que será más fácil analizar lo que está pasando en profundidad con menos que revisar. Aprendí mucho sobre cómo funciona Tor Browser Launcher y cómo configurar los perfiles de Firefox. Este perfil se ha recopilado de múltiples fuentes en línea y se mencionan en el código fuente y en este documento.
He abstraído el inicio, el desmontaje y una parte muy común de la lógica de navegación en una clase llamada TorDriver
. Es una clase muy simple que configura un perfil de Firefox con Tor Browser Launcher. Tiene un método para verificar si un elemento está visible en la página y otro que verifica que el socket del proxy esté funcionando. La configuración y depuración del perfil de Firefox se basó en gran medida en una discusión de Stack Overflow: Abrir el navegador Tor con Selenium .
El archivo completo se puede encontrar aquí: tor-driver-python/torDriver.py
Importación de selenio, pprint, subproceso y socket para la configuración y los componentes de WebDriver.
El siguiente método abstrae la comprobación de un elemento y devuelve True
o False
si está visible dentro de un tiempo de espera.
El puerto proxy debe estar activo antes de enviarle señales. Siguiendo algunos ejemplos en Stack Overflow sobre la prueba de conexiones de socket en Python, se me ocurrió esto:
La mayor parte del módulo es una clase que controla el perfil de Firefox, descarga geckodriver e inicia torbrowser-launcher.
Aquí tengo una configuración básica y algunas formas de anular las cosas, pero sobre todo manteniendo esto lo más simple posible:
El perfil de Firefox debe configurarse como mínimo para conectarse al puerto proxy, también había deshabilitado javascript con él.
Esto usa el perfil y el binario de TorDriver para inicializar un controlador
Agregar un método para descargar y extraer geckodriver en un subproceso. Vale la pena mencionar que, de alguna manera, cuando se ejecuta en el contenedor, tar.gz
ya no se comprime y simplemente requiere que se desarchive. Más información sobre el error está disponible aquí: stdin: not in gzip format error
Hasta que el socket responda, vuelva a intentar una conexión al puerto proxy:
En este ejemplo, tomé el siguiente enfoque de dos fases. La primera fase es la recopilación de información, y la siguiente fase es para procesar la información. De esta manera, no estoy atado a la conectividad de la red durante todo el proceso y puedo volver a intentar analizar los resultados tantas veces como sea necesario sin volver al material de origen.
El archivo completo se puede encontrar aquí: tor-driver-python/crawler.py
El rastreador lee un archivo de texto y usa esa información para completar consultas en la sesión de WebDriver. El estado del rastreo se guarda en una carpeta de archivos json uno por consulta. Intento hacer el procesamiento mínimo absolutamente necesario para exportar la información una vez y cualquier procesamiento posterior puede ocurrir en los datos existentes en lugar de volver a los sitios.
Estoy usando un archivo de texto para almacenar búsquedas. Elegí un archivo de texto porque es muy fácil de reestructurar. La edición de texto es una barrera baja para iniciar un rastreo con información nueva o para reanudar uno que falló a la mitad. Si este rastreador tuviera requisitos de datos más complejos, consideraría usar una base de datos en su lugar. Eso permitirá implementar una API para controlar los escaneos con una interfaz de usuario personalizada, para generar informes.
Los archivos de ejemplo ya están en la carpeta de resultados en el repositorio: tor-driver-python/results
En un rastreador más robusto, sugeriría usar una tecnología de base de datos real. Esto es suficiente para saber fácilmente dónde se detuvo la recopilación de datos y facilitar el reinicio.
El rastreador se puede ejecutar desde el contenedor con los siguientes comandos. El generador de informes requiere la presencia de archivos JSON; aquí se puede encontrar un archivo CSV de exportación de ejemplo:
Inicie el contenedor:
docker run -it --rm -p 5901:5901 -v "${HOME}/src":/src excitingtheory/kalilinux-xvfb:torbrowser
Inicie un servidor VNC en el contenedor, le pedirá contraseñas de sesión:
/opt/start-vnc-server-once.sh
Comience el rastreo desde dentro de la sesión de VNC:
python3 crawler.py
El rastreador esperará la inicialización de Tor Browser y, desafortunadamente, es un paso manual. Simplemente haga clic en la casilla de verificación y haga clic en conectar. Vea el video de demostración para ver un ejemplo.
La secuencia de comandos del informe generará un archivo de valores separados por comas (CSV) a partir del
Archivos de resultados de notación de objetos de JavaScript (JSON) que el rastreador guarda durante el rastreo. Elegí el formato CSV porque es un formato más común para compartir con colegas, pero aún así es fácil de importar a otras herramientas para un análisis más detallado.
El archivo completo se puede encontrar aquí: tor-driver-python/report.py
Esto utiliza bibliotecas de Python integradas para leer JSON, escribir CSV y analizar URL para formatear y presentar datos. Luego recorre los resultados y los carga para comenzar el procesamiento de datos.
Esta es la funcionalidad principal del generador de informes. Esto hace una presentación y ordenación final de los datos capturados en los objetos de resultados. Por lo general, las URL solo son útiles para el movimiento funcional de los rastreadores a través de un sitio, y no como una captura de datos final, pero es un buen comienzo para personalizar la extracción de datos adicionales.
Los resultados del rastreo se guardan en el directorio ./results
como archivos JSON. Usaré el siguiente script para generar un informe a partir de los datos.
python3 report.py
Puede encontrar un archivo CSV de salida de ejemplo aquí: tor-driver-python/output.csv
Hay algunas formas diferentes de detectar y mitigar la actividad de Bot. Me centraré principalmente en el lado del centro de datos, pero también discutiré algunos métodos de detección del lado del cliente. Sin embargo, nunca se puede confiar realmente en el cliente, ya que las señales del lado del cliente pueden cambiar en cualquier momento y pueden ser falsificadas. Creo que es importante tener esto en cuenta al diseñar un sistema de detección. En el centro de datos hay dos formas de protección que discutiré: limitación de velocidad y bloqueo de reputación.
Hay algunas formas de detectar una sesión activa de WebDriver en el lado del cliente con solo javascript: un problema algo relacionado en Github entra en más detalles . Esencialmente, debido a que el protocolo WebDriver cambia los objetos del documento y la ventana, se puede detectar en el código del lado del cliente.
Me centraré en las soluciones con las que tengo más experiencia, Fastly, AWS WAF y Nginx. CloudFlare fue una sorpresa total, así que también voy a hablar sobre su oferta.
Las reglas basadas en tasas del firewall de aplicaciones web (WAF) de AWS también se pueden usar para bloquear los niveles de actividad de denegación de servicio, y hay reglas predeterminadas que también se pueden usar para detectar el tráfico de la red Tor. Consulte la documentación de reglas de reputación de IP para obtener más información. Otro enfoque común es bloquear todo el tráfico de otros centros de datos, lo cual es seguro si el público objetivo son los consumidores. Sin embargo, las empresas podrían estar utilizando VPN en la nube y otra tecnología que puede hacer que esto sea perjudicial para el tráfico legítimo.
Signal Science de Fastly, una solución muy popular, se puede utilizar para detectar específicamente el tráfico de Tor. En primer lugar, pueden protegerse de los ataques DDOS; consulte la página Mitigación de DDOS para obtener más información. En segundo lugar, pueden detectar el tráfico de Tor y bloquearlo. Aquí está la documentación Uso de señales del sistema que cubre esto.
Para Nginx también hay algunos artículos sobre cómo hacer esto: Cómo bloquear el tráfico anónimo con Nginx o dentro de su aplicación web . Esencialmente, al llamar a las API para obtener información sobre los nodos de salida de Tor, las reglas de bloqueo de IP se pueden generar y aplicar a Nginx según un cronograma.
En un sorprendente contraste con los proveedores de nube anteriores, CloudFlare ofrece soporte para clientes Tor. ¡Me encontré con su documentación de soporte de Tor! donde discuten la capacidad de servir contenido a los usuarios de Tor desde la red. Creo que este es un enfoque realmente interesante, y estoy ansioso por explorarlo más a fondo en el futuro.
WebDriver es una poderosa herramienta para realizar pruebas y también se puede usar para recopilar información en lugares donde el acceso a una API no es factible. Por ejemplo: el acceso está restringido, censurado, es demasiado costoso o, en general, está bloqueado detrás de prácticas anticompetitivas. Mejor aún es combinar los datos recopilados del rastreo web con la información recopilada de las API.
Este es un ejercicio importante porque cada vez es más difícil evitar el tráfico malicioso de los bots, y no es una buena práctica de seguridad esperar hasta que ocurra un ataque para considerar cómo mitigarlo. Creo que todos los responsables de poner información en línea deben saber cómo se utilizará la información violada contra los sistemas de los que son responsables. En un escenario simplificado, con restricciones éticas, demostré esto haciendo lo siguiente: