Pruebas y verificación de programas

CEDI. The ideal of Verified Software. Un ejercicio curioso en Getting a program right, in nine episodes donde Bertrand Meyer nos acompaña donde desarrolla un programa sencillo y lo va mejorando mediante técnicas de verificación formal.

Aunque el testing ha ganado muchos adeptos hay que recordar lo que decía Dijkstra: las pruebas pueden demostrar la presencia de errores, no su ausencia.

in fact, I have always thought that the inevitable Dijkstra quote about testing — that it can only show the presence of errors, not their absence

Esto no es hablar mal de las pruebas, es sensacional poder encontrar partes de nuestros programas que están mal.

La verificación aporta otro tipo de valores, y de eso va lo que se puede leer allí. El programa es una búsqueda binaria, que es un concepto fácil de entender, pero no tan sencillo de hacer complemtamente bien.

Interesante.

Vulnerabilidades frecuentes en 'smart contracts'

Libro. Contrato con Dios De la mano de blockchain han venido los smart contracts: se trata de ‘programas’ o ‘codificaciones’ de intenciones, condiciones y consecuencias que gracias a esta tecnología permitirían que determinados contratos puedan ejecutarse sin necesidad de que los humanos intervengan (en la realización de los hechos del contrato en sí, se entiende).

Desde la ignorancia informada, uno siempre ha pensado que si se ha de codificar determinadas condiciones de la vida en un contrato la cosa ya es suficientemente compleja. Si a eso le añadimos la ejecución automatizada, es probable que haya que hilar más fino. Y, claro, aparecen los bugs y las vulnerabilidades.

De esto hablaban en Las 5 vulnerabilidades más habituales de los Smart Contracts y lo más gracioso es que los fallos se parecen mucho a los de los programas ‘normales’.

  • Errores aritméticos con números enteros

Desbordamientos, falta de precisión, …

  • Vulnerabilidades del límite de block gas

El límite de block gas es la forma que tiene Ethereum de asegurarse de que los bloques no crezcan demasiado. Simplemente significa que los bloques están limitados en la cantidad de gas que las transacciones contenidas en dichos bloques pueden consumir. En pocas palabras, si una transacción consume demasiado gas nunca cabrá en un bloque y, por lo tanto, nunca se ejecutará.

En determinadas circunstancias esto provoca que haya instrucciones de los contratos que nunca llegan a ejecutarse.

  • Falta de parámetros o controles de precondición

Si hace falta que algo se cumpla a la hora de ejecutar un contrato, debemos estar seguros de que los datos que se reciben son adecuados.

  • Frontrunning

El frontrunning potencial es probablemente el tema más difícil de prevenir en nuestra lista de vulnerabilidades comunes. Este puede definirse como la apropiación de una transacción no confirmada, y es consecuencia de la propiedad de transparencia del blockchain.

  • Bugs de lógica simples.

No hace falta buscar, de todas formas, fallos muy sofisticados. Parece que es frecuente encontrar:

… el problema más común que detectamos es la presencia de errores simples en la lógica del smart contract. Estos errores pueden ser el resultado de una simple errata, una mala comprensión de la especificación o un error de programación mayor …

Interesante.

Programación y coste en tiempo y en memoria

Memoria recursiva de Universa Esta entrada habla de Python, pero a veces por desconocimiento (o comodidad) no utilizamos todas las características de nuestros lenguajes favoritos y me pareció de interés.

En Reduce Memory Usage and Make Your Python Code Faster Using Generators hablan de los generadores, como un mecanismo para gastar menos memoria y generar un código más eficiente en Python.

Técnicamente, un generador es una función que utiliza yield en lugar de return nos dice el autor.

A generator looks a lot like a function, but uses the keyword yieldinstead of return. Let us take an example for better understanding.

Cuando hacemos una llamada tenemos un valor, pero con algunas característias interesantes. Por ejemplo, la función next(), que nos devolverá el siguiente valor generado por la función original (esto es, es una función que nos puede ir proporcionando valores sucesivos).

The important thing to note is how state is encapsulated within the body of the generator function. You can also step through one by one, using the built-in next() function:

En Python podemos utilizar la función range() para generar una sucesión de valores, que genera(ba) un coste importante (según el tamaño, claro) en espacio y tiempo al ejecutarse. Aunque allí no se dice, que hace mucho tiempo que teníamos xrange() y que en Python 3 range() también es un generador, con lo que el problema no sería tal.

En todo caso, parece que casi siempre merece la pena evaluar las consecuencias de las estructuras que utilizamos.

Programando criptografía

Puerta de Famagusta Uno de los consejos que se dan siempre en criptografía, es no inventar nada. El tema es suficientemente complejo para que alguien con poca experiencia pueda equivocarse y hacerlo mal. En Rolling your own encryption. What could possibly go wrong? hacen un pequeño recorrido (con código en Python) por diversos métodos de cifrado a modo de divertimento (y sin ninguna garantía de calidad).

Encryption is tricky to get right. Because some data and communications might be very sensitive or even life-critical for people, beginners are often - and quite rudely- shunned away from playing around with how it works. But if people don’t learn by making bad ciphers, they will have difficulty understanding why the good ones are good.

Gestores de contraseñas y seguridad

Entrada Uno de mis temas favoritos en cuanto a problemas de seguridad son los programas desarrollados para protegerla que resultan ser vulnerables. En esta ocasión podemos leer Some commercial password managers vulnerable to attack by fake apps que habla de gestores de contraseñas y algunas vulnerabilidades. Reconozcámoslo, la seguridad no es fácil: hay muchas cosas que se pueden hacer mal y es complicado hacerlo todo bien.

Algunos de estos programas utilizaban métodos débiles para identificar una aplicación y qué credenciales había que usar con ella (hacer las cosas más fáciles puede hacerlas demasiado fáciles).

… some of the password managers used weak criteria for identifying an app and which username and password to suggest for autofill. This weakness allowed the researchers to impersonate a legitimate app simply by creating a rogue app with an identical name.

Esto significa que si engañamos al usuario para que instale una aplicación falsa ‘adecuada’ podríamos conseguirlas.

“Our study shows that a phishing attack from a malicious app is highly feasible – if a victim is tricked into installing a malicious app it will be able to present itself as a legitimate option on the autofill prompt and have a high chance of success.”

La recomendación frente a esto es utilizar doble factor de autentificación: pueden robarnos las credenciales pero será difícil que tengan, además, el dispositivo para el segundo factor.

“The risk presented with autofill on compromised websites pertains only to the site’s credentials, not the user’s entire vault. It is always in the user’s best interest to enable MFA for all online accounts …

Había otros problemas, por ejemplo, no había limitaciones sobre el número de veces que se podía comprobar un PIN.

The researchers also discovered some password managers did not have a limit on the number of times a master PIN or password could be entered. This means that if hackers had access to an individual’s device they could launch a “brute force” attack, guessing a four digit PIN in around 2.5 hours.

Claro que, en este caso, el doble factor probablemente tampoco ayudaría.

Otro de mis favoritos en seguridad es lo que sucede muchas veces: problemas de seguridad detectados y anunciados anteriormente siguen allí.

The researchers also drew up a list of vulnerabilities identified in a previous study and tested whether they had been resolved. They found that while the most serious of these issues had been fixed, many had not been addressed.

Interesante.

Filtros de Bloom y optimización fallida

Tortuga y roca Los filtros de Bloom son conocidos para comprobar si un elemento está en un conjunto de manera eficiente de manera probabilista; esto es, el filtro puede responder cosas como ‘es posible que el elemento esté en el conjunto’ o ‘no está’. Se trata de obtener resultados cuando el conjunto de datos es tan grande que sería poco práctico utilizar otras técnicas más precisas.

En When Bloom filters don’t bloom nos cuentan sobre su uso en la detección de falseamientos de la IP (IP spoofing) para tomar decisiones sobre ellos en Cloudfare. El autor tenía un buen número de IPs recopiladas y al querer eliminar duplicados pensó que podrían servirle.

Nos cuenta detalles de sus desarrollos y la forma de hacerla más eficiente y como se encontró con un conjunto de datos que, aún así, no cabía en su memoria. Su programa invertía mucho tiempo moviendo datos entre las memorias disponibles.

Finalmente, indica algunas de las lecciones aprendidas:

El acceso a memoria secuencial y cuando se pueden predecir los siguientes accesos es algo que funciona bien en las CPUs modernas.

Modern CPUs are really good at sequential memory access when it’s possible to predict memory fetch patterns

Las estructuras de datos avanzadas son interesantes, pero hay que tener en cuenta el hardware y sus condicionantes (en este caso, las cachés).

Advanced data structures are very interesting, but beware. Modern computers require cache-optimized algorithms. When working with large datasets, not fitting L3, …

También algún comentario sobre el perfilado de programas.

Interesante.

Bloom filter

Equipos distribuidos y desarrollo

Teléfonos En For Distributed Teams, Code Craft is Critical hablaban de los equipos de desarrollo distribuidos (en lo que se han compartido la mayoría, al menos durante una buena parte de los últimos meses).

Nos habla de la disciplina necesaria y los factores que hay que tener en cuenta: pruebas unitarias, integración continua, desarrollo dirigido por pruebas, principios de diseño (simple, diseño modular, refactorización).

Cada uno de estos resuelve algunas cuestiones y creo que puede ser una lectura interesante, aunque no estemos en un equipo distribuido.

Proyecto de OWASP sobre seguridad de APIs

¿Qué haríamos sin enchufes? Otro proyecto intesesante de la OWASP, el OWASP API Security Project. Sería como el top-ten de seguridad, pero pensando en APIs. Un API (Application Programming Interface) es una forma en la que se pueden proporcionar determinados servicios a través de llamadas y peticiones a determinadas funcionalidades que pueden integrarse en nuestros propios programas, en lugar de tener que esperar a que el propietario de la información haga un programa que nos sirva para nuestras necesidades.

Incluye, claro, cuestiones muy similares a las generales en seguridad: autorización de acceso a los objetos incorrecta, autentificación incorrecta del usuario, exceso en la exposición de datos, problemas con escasez de recursos y limitaciones de acceso, problemas de autorización con respecto al nivel de acceso, asignaciones de datos poco controladas, mala configuración de seguridad, problemas de inyección, gestión inadecuada de recursos y falta de registro de actividad y monitorización.

API1:2019 Broken Object Level Authorization

API2:2019 Broken User Authentication

API3:2019 Excessive Data Exposure

API4:2019 Lack of Resources & Rate Limiting

API5:2019 Broken Function Level Authorization

API6:2019 Mass Assignment

API7:2019 Security Misconfiguration

API8:2019 Injection

API9:2019 Improper Assets Management

API10:2019 Insufficient Logging & Monitoring

Interesante.

Sobre el método usado para generar ficheros .zip

¡Oh! ¡Qué bonito!

Casi a título de inventario. Una necesidad frecuente en el mundo de la informática es el empaquetado (reunir varios ficheros/documentos para su distribución, por ejemplo, de manera conjunta) y la compresión (hacer que lo guardado ocupe el menor espacio posible).

En History, Explanation and Implementation nos cuentan el caso del formato zip que no es especialmente bueno ni moderno, pero que se puede entender bien con la voluntad adecuada. Incluye ejemplos de código:

This article explains how the Zip file format and its compression scheme work in great detail: LZ77 compression, Huffman coding, Deflate and all. It tells some of the history, and provides a reasonably efficient example implementation written from scratch in C.

La forma en que trabaja este método de compresión es extraer la información común, codificarla adecuadamente y sustituirla por códigos más breves cuando aparezca.

One way of compressing text is to maintain a list of common words or phrases, and replace occurrences of those words in the text with references to the dictionary. For example, a long word such as “compression” in the original text might be represented more efficiently as #1234, where 1234 refers to the position in the word list. This is known as dictionary-based compression.

Interesante.

Automatización de lo que se ve en el perfil de GitHub

Imagen de la página Programar me relaja y además me gusta hacer pruebas, aunque el tiempo disponible no sea demasiado. También me gustaría incluir en esta bitácora algunas de esas pruebas, por si le sirven a alguien para algo, que es algo que necesita todavía más tiempo. Esta entrada pretende mostrar una de esas pruebas, que permite actualizar automáticamente el perfil de GitHub gracias a algunas características que han incluido recientemente en esa ‘red social’ de desarrolladores.

Lo que cuento se basa en las indicaciones que se pueden encontrar en Building a self-updating profile README for GitHub (sigo el sitio de Simmon Willison desde hace mucho tiempo y fue una sorpresa agradable descubrir la receta y otras inspiraciones) y también en How I Built A Self-Updating README On My Github Profile (en este caso encontrado gracias a una búsqueda de Google y con algunas ideas de diseños más atractivos, al menos para mi).

GitHub ha lanzado recientemte el léeme del perfil (README) que permite utilizar markdown para incluir información personalizada en el perfil del usuario. Basta con crear un repositorio público nuevo con el nombre del propio usuario (en mi caso, github.com/fernand0/fernand0 e incluir un fichero README. GitHub utilizará este fichero para mostrarlo en nuestra página de perfil.

Siendo un repositorio, uno puede preguntarse (al menos Willison lo hizo) si puede automatizar algunas tareas relacionadas con el mismo. Y nos cuenta como esto es posible gracias a una acción de GitHub (GitHub Action) que se define en el fichero build.yml.

No voy a entrar en mucho detalle, pero contiene:

  • Formas de ‘disparar’ acciones (en nuestro caso, cuando se hace un push o basado en el tiempo, con la sintáxis del crontab).
  • Después, permite ejecutarlas (dónde se ejecuta -sistema y entorno de desarrollo, incluyendo instalación de paquetes si es necesario-: en mi caso en una Ubuntu con el lenguaje Python) y algunas cosas más (por ejemplo, definir TOKENS necesarios para realizar determinadas acciones -en nuestro caso, utilizar el Graph QL API de GitHub).
  • Finalmente, utilizar el resultado de esas acciones para generar la información que aparecerá en nuestro perfil (Fundamentalmente hacer un push si ha habido cambios).

Sobre mi actividad en el propio sitio, decidí incluir información relacionada con repositorios (contribuciones -mínimas- a los de otros proyectos y a mis propios repositorios públicos). Sobre otra actividad, pensé en incluir las últimas entradas en mis dos bitácoras más activas (incluye esta) y se muestran en la página.

El código de actualización está en build_readme.py (versión en el momento de escribir esta entrada del código basado en el de Willison, las partes mejor realizadas son mérito suyo, las más feas son mis propios ‘apaños’).

Para mostrar la información de los repositorios, como decía arriba, se utilizan el Graph QL API de GitHub con un token personal (en ‘Settings’ de la cuenta de GitHub podemos buscar la opción de ‘Developer settings’ y allí crear lo que llaman ‘Personal access tokens’. Luego hay que darle los permisos adecuados para este token (en mi caso los de usuario, los de workflow, y los de lectura y escritura en un repo -así a ojo, creo que son los necesarios; si no funciona ya nos dirá cuáles necesitamos-).

Para que el sistema que ejecuta nuestro programa tenga acceso al token podemos usar los ‘settings’ del proyecto (estos son los míos, pero se entiende, espero) y allí ir a la opción de ‘Secrets’ donde podemos dar de alta la información secreta que necesitemos.

En mi caso, la información que se muestra sobre la actividad en GitHub está en la pregunta:

query MyQuery {
  user(login: "fernand0") {
    repositoriesContributedTo(last: 20, orderBy: {field: PUSHED_AT, direction: DESC}) {
      edges {
        node {
          name
          description
          projectsUrl
          pushedAt
        }
      }
    }
    repositories(last: 10, orderBy: {field: UPDATED_AT, direction: ASC}, privacy: PUBLIC) {
      edges {
        node {
          name
          description
          projectsUrl
          owner {
            login
          }
          pushedAt
        }
      }
    }
  }
}

Para construirla GitHub nos proporciona una herramienta muy útil, que es el ‘GitHub GraphQL API’ que simplifica mucho la tarea de probar las preguntas.

Para mostrar la información de las bitácoras, utilizo el paquete ‘feedparser’ que ya usábamos hace algún tiempo en Publicar en Facebook las entradas de este sitio usando Python para extraer la información, formatearla y añadirla al README.

Por otro lado, de Hoffman he mirado los simbolitos sociales (y he descubierto Shields.io que parece un sistema de generación de logos utilizando SVG y otros trucos; he usado algunos que ya tenía Hoffman y he ‘añadido’ otros que no existían, no se qué sucederá) y el formato en una sola columna (Willison tiene una tabla con tres columnas que no se ven muy bien, por ejemplo, en el navegador de un teléfono móvil).

¿Qué pasará a partir de ahora?

Probablemente seguiré jugando con el aspecto para dejar uno que me convenza del todo y, tal vez, añada algún servicio más. Uno evidente es el de Twitter, pero podría ser otra cosa.

Si alguien necesita ayuda con algún paso, puede leer con calma las entradas recomendadas y también preguntar, si lo que necesita no es muy diferente de lo que se puede ver aquí.