Compartir:
El software es una cosa curiosa y profunda: cada pieza de él es una máquina invisible, aparentemente hecha de palabras mágicas, diseñada para ejecutarse en la máquina definitiva y universal. No está vivo, pero tiene un ciclo de vida. Comienza como código fuente—simplemente archivos de texto, alojados en un repositorio en algún lugar—y luego, más tarde (a través de algún proceso único), ese código fuente se «construye» en algo diferente. Un trozo de Javascript minimizado entregado a un servidor web, una imagen de contenedor llena de código de marco y lógica empresarial, un binario sin procesar compilado para una arquitectura de procesador específica. Esa etapa final de metamorfosis, eso en lo que se convierte el código fuente, es lo que normalmente llamamos «artefacto de software». Estos artefactos, tras su creación, suelen pasar un buen periodo de tiempo en reposo, esperando ser utilizados. Lo hacen en registros de paquetes (como npm, RubyGems, PyPI, MavenCentral, etc.) o en registros de contenedores (como GitHub Packages, Azure Container Registry, AWS ECR, etc.), como binarios adjuntos a GitHub Releases, o simplemente como un archivo ZIP almacenado en algún almacenamiento de blobs.
Eventualmente, alguien decide tomar ese artefacto y usarlo. Descomprimen el paquete, ejecutan el código, lanzan el contenedor, instalan el controlador, actualizan el firmware—sin importar la modalidad, de repente la cosa construida está en funcionamiento. Esta es la culminación de un ciclo de vida de producción que puede tomar muchas horas humanas, costar mucho dinero y, dada la dependencia del mundo moderno en el software, puede ser de altos riesgos.
Y sin embargo, en muchos casos, no tenemos una garantía sólida de que el artefacto que ejecutamos sea exactamente lo que construimos. Los detalles del viaje de ese artefacto se pierden, o en el mejor de los casos son borrosos; es difícil conectar el artefacto de vuelta al código fuente y las instrucciones de construcción de donde provino. Esta falta de visibilidad en el ciclo de vida del artefacto es la fuente de muchos de los desafíos de seguridad más apremiantes de hoy. A lo largo del ciclo de vida del desarrollo de software (SDLC), hay oportunidades para asegurar el flujo de código que se transforma en artefactos, lo que ayuda a eliminar el riesgo de que actores maliciosos envenenen el software finalizado y creen caos.
Algunos desafíos en la ciberseguridad pueden parecer casi imposibles de abordar con éxito, pero este no es uno de ellos. Vayamos al fondo con algo de contexto.
Digamos que tienes un archivo en tu directorio personal y quieres asegurarte de que sea exactamente el mismo mañana que hoy. ¿Qué haces? Una buena manera de comenzar es generar un digest del archivo ejecutándolo a través de un algoritmo hash seguro. Aquí se muestra cómo podemos hacerlo con OpenSSL, utilizando el algoritmo SHA-256:
openssl dgst -sha256 ~/important-file.txt
Ahora, tienes un digest (también llamado hash), una cadena de 64 caracteres que representa una huella digital única para ese archivo. Cambia literalmente cualquier cosa en ese archivo, ejecuta la función de hash de nuevo, y obtendrás una cadena diferente. Puedes escribir el digest en algún lugar, volver mañana y probar el mismo proceso nuevamente. Si no obtienes la misma cadena de digest ambas veces, algo en el archivo ha cambiado.
Bien, hasta ahora, tan bien: podemos determinar si algo ha sido manipulado. ¿Qué pasa si queremos hacer una declaración sobre el artefacto? ¿Qué pasa si queremos decir «vi este artefacto hoy, y yo (un sistema o una persona) garantizo que esta cosa en particular es definitivamente la cosa que vi»? En ese punto, lo que quieres es una firma de artefacto de software; quieres tomar tu cadena de digest y ejecutarla a través de un algoritmo criptográfico para producir otra cadena que represente el acto de «firmar» esa huella digital con una clave única. Si posteriormente quieres que otra persona pueda confirmar tu firma, tendrás que usar cifrado asimétrico: firma el digest con tu clave privada y entrega la clave pública correspondiente para que cualquier persona en el mundo que obtenga tu archivo pueda verificarlo.
Probablemente ya sepas que la encriptación asimétrica es la base de casi toda la confianza en Internet. Es cómo puedes interactuar de manera segura con tu banco y cómo GitHub puede entregar de manera segura los contenidos de tu repositorio. Usamos la encriptación asimétrica para impulsar tecnologías, como TLS y SSH, para crear canales seguros de comunicación, pero también la usamos para crear una base de confianza en el software a través de firmas.
Los sistemas operativos como Windows, macOS, iOS, Android, etc., todos tienen mecanismos para garantizar un origen de confianza para los artefactos de software ejecutables al exigir la presencia de una firma. Estos sistemas son componentes increíblemente importantes del mundo del software moderno y construirlos es extremadamente difícil.
Cuando pensamos en cómo exponer más información confiable sobre un artefacto de software, una firma es un buen comienzo. Dice «algún sistema de confianza definitivamente vio esta cosa». Pero si realmente quieres ofrecer un salto evolutivo en la seguridad del SDLC en su conjunto, necesitas ir más allá de las meras firmas y pensar en términos de atestaciones.
Una atestación es una afirmación de un hecho, una declaración hecha sobre un artefacto o artefactos y creada por alguna entidad que puede ser autenticada. Puede ser autenticada porque la declaración está firmada y la clave que hizo la firma puede ser confiable.
El tipo más importante y fundamental de atestación es aquella que afirma hechos sobre el origen y la creación del artefacto: el código fuente del que proviene y las instrucciones de construcción que transmutaron ese origen en un artefacto. A esto lo llamamos una atestación de procedencia.
La especificación de atestación de procedencia que hemos elegido proviene del proyecto SLSA. SLSA es una excelente manera de pensar sobre la seguridad de la cadena de suministro de software porque ofrece a los productores y consumidores de software un marco común para razonar sobre las garantías de seguridad y los límites de una manera que sea agnóstica de sistemas y pilas tecnológicas específicas. SLSA ofrece un esquema estandarizado para producir atestaciones de procedencia para artefactos de software, basado en el trabajo realizado por el proyecto in-toto. in-toto es un proyecto graduado por CNCF que existe (entre otras cosas) para proporcionar una colección de esquemas de metadatos estandarizados para información relevante sobre tu cadena de suministro y proceso de construcción.
Como la mayor plataforma global de desarrollo de software que alberga mucho código y pipelines de construcción, hemos estado pensando mucho en esto. Hay una serie de partes móviles que se necesitarían para construir un servicio de atestación.
Hacerlo significaría tener una forma de:
Emitir certificados (esencialmente claves públicas vinculadas a alguna identidad autenticada).
Asegurar que esos certificados no puedan ser mal utilizados.
Permitir la firma segura de artefactos en un contexto conocido.
Verificar esas firmas de una manera en la que el usuario final pueda confiar.
Esto implica establecer una Autoridad de Certificación (CA) y tener alguna aplicación cliente que puedas usar para autenticar las firmas asociadas con los certificados emitidos por esa autoridad. Para evitar que los certificados sean mal utilizados, necesitas o bien mantener Listas de Revocación de Certificados o asegurar que el certificado de firma tenga una vida corta, lo que significa tener una firma secundaria de alguna autoridad de estampado de tiempo (que pueda dar un sello autoritativo de que un certificado solo se usó para producir una firma durante el período en que era válido).
Aquí es donde entra Sigstore. Es un proyecto de código abierto que ofrece tanto una CA X.509 como una autoridad de estampado de tiempo basada en RFC 3161. Y también te permite hacer identidad con tokens OIDC, que muchos sistemas de CI ya producen y asocian con sus cargas de trabajo.
Sigstore hace por las firmas de software lo que Let’s Encrypt ha hecho por los certificados TLS: hacerlos simples, transparentes y fáciles de adoptar. GitHub ayuda a supervisar la gobernanza del proyecto Sigstore a través de nuestro asiento en el Comité Técnico de Dirección, somos mantenedores de las aplicaciones del servidor y varias bibliotecas de clientes, y (junto con personas de Chainguard, Google, RedHat y Stacklok) formamos el equipo de operaciones para la Instancia de Bien Público de Sigstore, que existe para apoyar atestaciones públicas para proyectos de código abierto.
Sigstore requiere una raíz de confianza segura que cumpla con el estándar establecido por The Update Framework (TUF). Esto permite a los clientes mantenerse al día con las rotaciones en las claves subyacentes de la CA sin necesidad de actualizar su código. TUF existe para mitigar un gran número de vectores de ataque que pueden surgir al intentar actualizar el código in situ. Es usado por muchos proyectos para actualizar cosas como agentes de telemetría de larga duración, entregar actualizaciones de firmware seguras, etc.
Con Sigstore en su lugar, es posible crear un rastro de papel a prueba de manipulaciones que vincule los artefactos de vuelta al CI. Esto es realmente importante porque firmar software y capturar detalles de procedencia de una manera que no pueda ser falsificada significa que los consumidores de software tienen los medios para hacer cumplir sus propias reglas con respecto al origen del código que están ejecutando, y estamos emocionados de compartir más con ustedes en los próximos días. ¡Manténganse atentos!
Aprovecha el poder de GitHub Advanced Security. Aprende más o comienza ahora.