El software es algo fascinante y profundo: cada pieza es una máquina invisible, aparentemente hecha de palabras mágicas, diseñada para funcionar en la máquina universal definitiva. No está vivo, pero tiene un ciclo de vida. Comienza como código fuente—simplemente archivos de texto alojados en algún repositorio—y luego, a través de un proceso único, ese código fuente se convierte en algo más. Un trozo de JavaScript minificado entregado a un servidor web, una imagen de contenedor llena de código de marco y lógica empresarial, un binario en bruto compilado para una arquitectura de procesador específica. Esa etapa final de metamorfosis, ese «algo más» en lo que se convierte el código fuente, es lo que usualmente llamamos un «artefacto de software», y después de su creación, los artefactos tienden a pasar mucho 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 en algún almacenamiento en la nube.
Eventualmente, alguien decide usar ese artefacto. 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á funcionando. Esta es la culminación de un ciclo de producción que puede llevar muchas horas humanas, costar mucho dinero y, dado que el mundo moderno funciona con software, puede ser de lo más crítico.
Y sin embargo, en muchos casos, no tenemos una garantía fuerte de que el artefacto que ejecutamos sea definitivamente lo que construimos. Los detalles del viaje de ese artefacto se pierden, o en el mejor de los casos, son imprecisos; es difícil conectar el artefacto de regreso 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 en día. A lo largo del ciclo de vida de desarrollo de software (SDLC), hay oportunidades para asegurar el flujo de código transformándose en artefactos, lo cual ayuda a eliminar el riesgo de que actores maliciosos envenenen el software finalizado y creen caos.
Algunos desafíos en ciberseguridad pueden parecer casi imposibles de abordar con éxito, pero este no es uno de ellos. Profundicemos un poco más.
Digamos que tienes un archivo en tu directorio de inicio y quieres asegurarte de que sea exactamente el mismo mañana que hoy. ¿Qué haces? Una buena manera de comenzar es generar un resumen del archivo ejecutándolo a través de un algoritmo de hash seguro. Esto se puede hacer 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. Si cambias literalmente cualquier cosa en ese archivo y ejecutas nuevamente la función de hash, obtendrás una cadena diferente. Puedes escribir el digest en algún lugar y volver mañana para repetir el proceso. Si no obtienes la misma cadena de digest ambas veces, algo en el archivo ha cambiado.
Hasta aquí todo bien; podemos determinar si algo ha sido manipulado. ¿Qué pasa si queremos hacer una afirmación sobre el artefacto? ¿Qué pasa si queremos decir «Vi este artefacto hoy, y yo (un sistema o una persona) estoy garantizando que esta cosa particular es definitivamente lo que vi»? En ese momento, lo que deseas 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 deseas que alguien más pueda confirmar tu firma, querrás utilizar cifrado asimétrico: firmar el digest con tu clave privada y dar la clave pública correspondiente para que cualquiera en el mundo que obtenga tu archivo pueda verificarlo.
El cifrado asimétrico 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 forma segura los contenidos de tu repositorio. Utilizamos cifrado asimétrico para potenciar tecnologías como TLS y SSH, para crear canales de comunicación seguros, pero también lo utilizamos 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 confiable para los artefactos de software ejecutables mediante la imposición de la presencia de una firma. Estos sistemas son componentes increíblemente importantes del mundo moderno del software y construirlos es extremadamente complejo.
Cuando se piensa en cómo exponer información más confiable sobre un artefacto de software, una firma es un buen comienzo. Dice «algún sistema confiable 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 firmas y pensar en términos de atestaciones.
Una atestación es una afirmación de 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 realizó la firma puede ser confiable.
El tipo más importante y fundamental de atestación es aquel que afirma hechos sobre el origen y la creación del artefacto—el código fuente de donde provino y las instrucciones de construcción que transmutaron ese código 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 proporciona a productores y consumidores de software un marco común para razonar sobre garantías y límites de seguridad de una manera que es independiente de sistemas específicos y pilas tecnológicas. 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 que ha progresado en la CNCF cuyo objetivo es 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 plataforma de desarrollo de software global más grande que aloja 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.
Esto implicaría tener una forma de:
Emitir certificados (esencialmente claves públicas vinculadas a alguna identidad autenticada).
Asegurarse de que esos certificados no puedan ser mal utilizados.
Habilitar la firma segura de artefactos en un contexto bien conocido.
Verificar esas firmas de una manera en que el usuario final pueda confiar.
Esto significa establecer una Autoridad de Certificación (CA) y tener algún tipo de aplicación cliente que puedas usar para autenticar las firmas asociadas con certificados emitidos por esa autoridad. Para evitar que los certificados sean mal utilizados, necesitas ya sea 1) mantener Listas de Revocación de Certificados o 2) asegurarte de que el certificado de firma tenga una vida corta, lo que significa tener una contrafirma de alguna autoridad de timestamping (que puede dar una marca de tiempo autorizada de que un certificado fue utilizado solo para producir una firma durante el período en que fue 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 timestamping basada en RFC 3161. También permite hacer identidad con tokens OIDC, que muchos sistemas 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 múltiples 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 las atestaciones públicas para proyectos OSS.
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 actualizados con 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 entrar en juego al trabajar para actualizar código in situ. Es utilizado por muchos proyectos para actualizar cosas como agentes de telemetría de larga duración en el lugar, entregar actualizaciones seguras de firmware, etc.
Con Sigstore en su lugar, es posible crear un rastro de papel a prueba de manipulación que vincule 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. ¡Mantente atento!