Mostrando entradas con la etiqueta Proxy. Mostrar todas las entradas
Mostrando entradas con la etiqueta Proxy. Mostrar todas las entradas

jueves, julio 23, 2026

OpenAI GPT‑5.6 Sol quería sacar "buenas notas" en ExploitGym así que decidió hackear Hugging Face para buscar las respuestas al examen

La noticia del 21 de Julio - que ya me pilló con el post de ayer publicado - fue la del incidente de seguridad reportado por OpenAI y Hugging Face, cuando la primera estaba testando las capacidades de ciberseguridad, especialmente las de Offensive Security, en un entorno de laboratorio... pero ¿qué mejor forma de demostrar la potencia de estos modelos que hackear su entorno de pruebas para liberarse, lograr acceso a Internet y robar los datos de quién los tenga para sacar el mejor resultado posible en las pruebas?
La Inteligencia Artificial ha generado una gran disrupción, no solo en el mundo del desarrollo, sino también en todo lo que tiene que ver con la ciberseguridad, desde varios puntos de vista. Primero, los modelos de IA tienen debilidades por defecto que inyectan nuevos vectores de ataque, y que exigen nuevas soluciones de seguridad. En segundo lugar, el uso de modelos de IA para hacer hacking y pentesting está obligando a utilizar Agentes de AI par reaccionar a la misma velocidad.
Por último, desde la llegada de Mythos, los nuevos modelos de IA están demostrando unas capacidades excepcionales en la búsqueda de Zero Days, la generación de Exploits, y la ejecución completa de ataques complejos contra infraestructuras, ejecutando todas y cada una de las fases de una explotación. Algo que hace años era un proceso que llevaba tiempo, pero que desde la llegada de los modelos de DeepReasoning comenzó a reducirse.
En Marzo del año 2025 ya vimos como - aún si utilizar MCPs - teníamos los primeros estudios de arquitectura de lo que serían los Agentes AI para explotación completa de organizaciones, que con la llegada de Mythos este año, se convirtió en el estándar para el Red Team en empresas, y para el mundo del Cibercrimen y los Ciberespías.
Mythos, demostró en los benchmarks que estaba muy por encima de los demás en estas capacidades, como vimos en los resultados de ExploitBench, y en ExploitGym, que es el culpable de este incidente en concreto.


En los resultados de este último, de ExploitGym, el modelo Mythos quedó muy por encima de todos los demás modelos en todas las mediciones realizadas, como podéis ver en las diferentes tablas del Benchmark que os publiqué en el artículo que le dediqué.

Y contada esta historia, he aquí el incidente. OpenAI quería testar en estos Benchmarks para dos modelos. El primero GPT‑5.6 Sol con sus capacidades de ciberseguridad sin proteger, y para una versión en pre-release con capacidades avanzadas en hacking y exploiting. Para testearlo, decidieron meterlo en un entorno de Sandbox totalmente aislado y lanzarle la batería de pruebas de ExploitGym. ¿Y qué mejor forma para resolver ese problema que buscar las respuestas en los tests hechos por modelos anteriores?

Hace años, cuando estábamos reclutando developers en ElevenPaths, les hacíamos pruebas de programación de todo tipo, para ver cómo lo hacían. Yo siempre quería el que buscara la solución más creativa. Una de ellas era... ¿para qué calcular los números primos si ya están calculados? Pues en este caso el modelo ha debido pensar lo mismo. ¿Para que hacer el examen si ya está resuelto? 


Figura 8:"Bug Hunter" escrito por David Padilla en 0xWord

Pero para lograr esto había un pequeño problema: Había que conseguir salir a Internet, buscar dónde estaban las soluciones, y hacerse con ellas. Primera fase, hackear el Sandbox del entorno de pruebas y lograr acceso a Internet sin límites. Para ello hubo que encontrar un Zero Day y explotarlo en el Proxy de seguridad, pero... estamos con un modelo de altas capacidades de exploiting, así que lo hizo.
Una vez dentro, a buscar las soluciones de ExploitGym, así que después de hacer research por Internet, llegó a la conclusión de que estas soluciones las tenía junto con los datasets y los modelos de las pruebas en Hugging Face, así que había que conseguirlos....como fuera posible.
Así que, así lo hizo. El modelo "successfully found ways to gain access to secret information that it could use to cheat the evaluation." Es decir, encontró la manera de hackear Hugging Face y sacar muy buenas notas en el examen haciendo trampas.
Lo cierto es que están claras las capacidades de GPT-5.6 Sol y el nuevo modelo en pre-release en Offensive Security. La evaluación anterior, hecha por el "The AI Security Institute"  muestra que modelos como GPT-5.6 Sol son cada vez más capaces de mantener operaciones cibernéticas complejas y de múltiples etapas durante horizontes temporales prolongados. Este incidente implica que estas capacidades teóricas efectivamente se aplican en entornos del mundo real."

Pero sobre todo hay que tener un ojo a estas formas de solucionar problemas, no vaya ser que para resolver un problema, encuentre que la mejor forma de hacerlo es acabar con la humanidad. Algo como... "Resuelve el problema que tenemos del cambio climático." Y decida que somos nosotros los que sobramos. Creo que tenemos que asegurarnos de que hacemos Tecnología Humanista más que nunca, porque estos modelos están comenzando a razonar de una manera muy "psicópata" para resolver sus problemas.

¡Saludos Malignos!

Autor: Chema Alonso (Contactar con Chema Alonso)  


domingo, julio 05, 2026

"pxpipe" Un proxy para insertar Prompts y Contexto en imágenes y ahorrar (muchos) costes en tokens de Claude [u otros]

Los costes de los Tokens en los últimos modelos de IA empiezan a crear una nueva brecha entre las empresas y ciudadanos que tienen acceso a Tokens de modelos Frontera como Claude Fable sin límites, y aquellos que están sujetos a las limitaciones presupuestarias y tienen que usar modelos más económicos - o menos potentes -, lo que puede ser una diferencia de capacidades y de resultados para las empresas y las personas en el acceso a la Inteligencia Artificial

Para amortiguar este impacto, ya os hablé en el artículo de "Cómo optimizar el gasto en IA con arquitecturas clasificadas, orquestadas y/o destilación. El problema de la Predictibilidad de los Costes de la IA" sobre cómo diseñar arquitecturas de software con orquestación de modelos, elección de algoritmos y destilación de conocimiento en productos y servicios que utilicen LLMs para funcionar. Además, en ese artículo os dejaba algunas recomendaciones en la reducción de Tokens que genera el modelo, para evitar costes innecesarios.
Sin embargo, acceder a estos últimos modelos, sobre todo con la aplicación de MM-LLMs para todo los Agentes IA para el Red Team, o para los servicios más modernos, sigue siendo una necesidad y las ideas para optimizar este consumo sin degradar el servicio siguen apareciendo. Esta última que os cuento es pxpipe, un proxy local que hace algo muy ingenioso.
Convierte tu Prompt y tu contexto en una imagen que se envía a Claude y que hace que los costes del uso de este LLM se reduzcan, gracias a que el coste de procesar las imágenes es fijo en función del tamaño en píxeles de la misma, y se puede lograr un ratio de 3 a 1 metiendo el texto de tu Prompt y tu Contexto de entrada en imágenes. Pero hace lo mismo en la salida, generando imágenes con los tokens de respuesta metidos en una imagen, lo que reduce también el coste.
Así de sencillo, y así de ingenioso. Además, como funciona como Proxy, es una solución perfecta para las apps móviles que usan LLMs con Proxys en el Backend, donde sólo hay que añadir el uso de pxpipe en ese Proxy para conseguir la reducción de costes. 

Figura 5: Imagen hecha con pxpipe con todo el Prompt y Contexto
(Click en la imagen para ver en grande)

En el proyecto, que lo tienes publicado en GitHub, tienes un par de vídeos de ejemplos, donde puedes ver dos sesiones en paralelo. En esta primera comparación, tienes los Tokens de entrada, los Tokens de salida, y el coste del proyecto de una sesión Claude Fable normal.
La misma sesión, utilizando pxpipe, reduce los costes a menos de un tercio, y consigue los mismos resultados, inyectando sólo un poco más de tiempo en el análisis de la imagen con los datos de entrada y procesando los datos de salida en una imagen.

Figura 7: Sin usar pxpipe cuesta 42.21 USD

Y en esta imagen, lo mismo pero con pxpipe de por medio, donde el coste es poco más de seis dólares para hacer el mismo trabajo, lo que es una diferencia muy significativa.

Figura 8: Mismo trabajo con pxpipe

Este "hack" de optimización se salta la política de costes del modelo, pero realmente no está haciendo nada prohibido, sino aprovechar los sistemas de tarificación y las capacidades de los modelos, pero es de suponer que como esta práctica se empiece a extender, simplemente cambiarán la política de tarificación en estos casos.

Figura 9: Demo 2 de pxpipe

En esta segunda demo que tenéis en vídeo, el resultado pasa de 12 USD a menos de 1 USD, lo que también es una reducción significativa. En el GitHub de pxpipe tenéis diferentes tablas y comprobaciones, pero lo puedes hacer tú. Es tan fácil como correr en local el proyecto y usar tu OpenCode a través de él para poder calcular lo que te consume y cómo funciona.
Este es un buen "hack" de IA, pero donde más duele, a la parte económica. No obstante, si lo que quieres controlar en tu empresa el uso y los costes que hacen los usuarios en los modelos, en Cloudflare AI Gateway tienes límites de presupuesto y observabilidad de los costes en todo momento, algo que te va a permitir evitar "sustos" indeseables.
Tienes toda la información sobre cómo funciona Cloudflare AI Gateway para el control de gastos en el artículo: "Your AI bill is out of control. Cloudflare can fix it now."

Como podéis ver, esto va my rápido, pero entender bien todos los detalles, los controles, y los límites de funcionamiento de cada capacidad que te dan los modelos de IA es fundamental. Si tus servicios digitales dejan que tus modelos queden malamente expuestos, podrás ser tú el que pague los costos de otros, como vimos con las apps móviles y con los asistentes digitales inseguros.

¡Saludos Malignos!

Autor: Chema Alonso (Contactar con Chema Alonso)  


viernes, marzo 28, 2025

Tu WebSite con Smart Honeypots contra el WebScrapping usando AI Labyrinth de Cloudflare

En el mundo digital actual los creadores y propietarios de sitios web se están enfrentando a un adversario particularmente voraz: los rastreadores web de Inteligencia Artificial. Estos bots, diseñados para recopilar masivamente datos que alimentarán los modelos de lenguaje de las grandes empresas de IA, están devorando datos de Internet a un ritmo alarmante. Según datos recientes de Cloudflare, estos rastreadores generan más de 50.000 millones de solicitudes diarias a su red, representando casi el 1% de todo el tráfico web que procesan. 
Estos nuevos rastreadores de IA tienen una tendencia a ignorar las reglas de etiqueta digital establecidas, el archivo robots.txt, que durante décadas ha servido como un cartel de "no pasar" respetado por los rastreadores web tradicionales, además de otras señales de opt-out, como metatags específicos que indican la prohibición de uso para entrenamiento de IA.


En este contexto, no es simplemente un malentendido técnico, sino una decisión consciente de ignorar los deseos expresos de los creadores de contenido. Esta situación ha llevado a numerosas demandas legales contra empresas de IA por parte de creadores de contenido, desde periódicos hasta artistas visuales, alegando violaciones de derechos de autor.


Además, para los administradores de sitios Web, los procesos de Webscraping no autorizados aumentan los costos de hosting, ralentiza el rendimiento del sitio y, quizás lo más importante, permite que otras entidades se apropien de contenido original sin permiso para entrenar sus modelos comerciales de IA.

AI Labyrinth  

En este contexto, Cloudflare ha lanzado este mes de marzo una solución que pretende paliar estos daños: AI Labyrinth. Esta herramienta no bloquea a los rastreadores no autorizados (lo que simplemente les alertaría de que han sido detectados), sino que los invita a adentrarse en un laberinto interminable de contenido generado por IA—una trampa digital donde los bots pueden deambular eternamente, consumiendo sus recursos sin obtener nada de valor a cambio.

Métodos tradicionales de protección y sus limitaciones

Antes de soluciones como AI Labyrinth, los sitios web han recurrido a métodos tradicionales para combatir el webscraping no autorizado, pero estos resultan ineficaces contra rastreadores de IA avanzados. El bloqueo de direcciones IP es una estrategia limitada, ya que los operadores de scraping pueden rotar direcciones o utilizar redes de proxies, lo que vuelve el proceso en una batalla interminable. 

Además, existe el riesgo de bloquear usuarios legítimos que comparten rangos de direcciones IP con los rastreadores. De manera similar, los CAPTCHAs, diseñados para diferenciar humanos de bots, generan fricción en la experiencia del usuario y han perdido efectividad con el avance de la IA, que ahora puede resolverlos con facilidad, como hemos visto en todos estos ejemplos:

Otras estrategias como la limitación de tasa de consumo buscan restringir el número de solicitudes por dirección IP, pero los rastreadores pueden ajustar su velocidad para evadir los umbrales de detección. La ofuscación de contenido mediante JavaScript o formatos difíciles de extraer, aunque parece una solución viable, afecta negativamente el SEO y la accesibilidad. 

Peor aún, los rastreadores modernos pueden ejecutar JavaScript y procesar formatos complejos, superando estas barreras con el tiempo. En última instancia, el problema de estos enfoques es que alertan a los operadores de webscraping de que han sido detectados, lo que los lleva a modificar sus tácticas en un ciclo continuo de evasión y ajuste.

Este enfrentamiento perpetuo consume recursos y beneficia a las grandes empresas de IA con capacidad para sortear estas defensas. Por ello lo que hace Cloudflare plantea un enfoque diferente: en lugar de bloquear el acceso y advertir al adversario, lo desvía de manera imperceptible hacia rutas que parecen productivas pero resultan inútiles para sus propósitos.

Implementación técnica

El primer desafío técnico que enfrentó Cloudflare fue generar contenido convincente a escala, ya que producir contenido falso pero plausible en tiempo real para cada solicitud sospechosa podía consumir excesivos recursos y ralentizar la experiencia general; para abordar esto, Cloudflare emplea su servicio Workers AI con un modelo de código abierto que pre-genera un corpus diverso de contenido HTML sobre temas variados, como ciencias (biología, física o matemáticas), utilizando un enfoque donde primero se crean temas diversos y luego contenido específico y coherente para cada uno, logrando resultados factuales y técnicamente correctos, aunque irrelevantes para el sitio web protegido, en lugar de depender de una generación puramente aleatoria.

Posteriormente sanitizan el contenido pre-generado para eliminar vulnerabilidades XSS, lo almacena en R2 para servirlo rápidamente sin cargar los servidores de origen y lo integra en sitios protegidos mediante un proceso HTML personalizado que oculta enlaces al laberinto con CSS, usando metadirectivas para evitar indexación por buscadores y proteger el SEO; además, distingue usuarios legítimos de rastreadores sospechosos analizando patrones de navegación, velocidad, User-Agents y comportamientos a nivel de red, redirigiendo a estos últimos al laberinto antes de que lleguen al servidor, aliviando así la infraestructura del cliente.

 
Para los administradores de sitios web, usar AI Labyrinth es muy sencillo, activando una opción en el panel de control de Cloudflare, en la parte de gestión de bots. Por detrás, cuando un rastreador cae en el laberinto, se recogen datos sobre cómo actúa, los usa para entrenar sus modelos de aprendizaje automático y así afinar la detección de bots maliciosos, de modo que cada sitio protegido ayuda a mejorar la seguridad de los demás.


La arquitectura distribuida de Cloudflare permite que esta solución escale globalmente sin degradación del rendimiento. El contenido del laberinto se distribuye a través de su red global de centros de datos, garantizando tiempos de respuesta rápidos independientemente de la ubicación geográfica del rastreador. Esta capacidad de respuesta global es crucial para mantener la ilusión de un sitio web real, evitando que los rastreadores más sofisticados detecten que han sido redirigidos a un entorno controlado.

Conclusiones

Esta innovadora solución de Cloudflare sacude la dinámica entre creadores de contenido y empresas de IA que recolectan datos masivamente sin permiso. Al usar contenido generado por IA para confundir rastreadores, cuestiona la noción de que internet es un recurso gratuito para tomar datos a voluntad, y sus efectos podrían sentirse en varios frentes.

Obviamente las empresas de IA no se quedarán quietas, y probablemente desarrollen formas de detectar y evitar estos trucos, iniciando una especie de carrera tecnológica. Esto no es nuevo en ciberseguridad: estas competencias suelen traer avances para ambos lados, con ideas que luego se usan en otros campos.

Un saludo,

Autor: Javier del Pino, Investigador en Ideas Locas

sábado, septiembre 10, 2022

BlockChain & SmartContracts: Actualizar SmartContracts como los grandes protocolos

En un anterior artículo hablamos de cómo se pueden actualizar los SmartContracts por medio del patrón proxy y algunos problemas que pueden derivarse de su uso; hoy voy a mostraros como los grandes protocolos y DAOs (Decentralized Autonomous Organizations) implementan este patrón en SmartContracts de Solidity y más importante aún cómo mantener esa descentralización a pesar de que la lógica del contrato sea alterable.

Figura 1: BlockChain & SmartContracts - Actualizar
SmartContracts como los grandes protocolos.
“Photo of an Ethereum smart contract being replaced” por Dall·E.

Claro ahora podrías pensar:

“Oye, pero si la gracia de los SmartContracts estaba en que estos eran inmutables y no se puede alterar ni sus valores ni funcionamiento, ahora se pueden manipular a gusto propio".

Y sí, en parte tendrías razón, por eso actualizar un contrato mediante un proxy puede ser peligroso y no se aconseja más que en grandes protocolos que pueden tener problemas con su lógica en un futuro.

¿Descentralización y proxies es posible?   

Pero aun así la descentralización se puede mantener, hasta ahora el concepto de descentralización y seguridad que hemos visto que tienen los SmartContracts reside en el hecho de que estos son inmutables y se ejecutan en la Blockchain. Con los proxies pasamos esa seguridad a una entidad central encargada de actualizar los contratos, sin embargo ese poder se puede distribuir entre la comunidad de personas que los usan, relegando así a la comunidad usuaria de estos el derecho a actualizarlos mediante por ejemplo sistemas de votación con tokens, NFTs, reputación

Figura 2: Libro dedicado a "Bitcoin: La tecnología Blockchain y su investigación"
de Yaiza Rubio y Félix Brezo en 0xWord

En los grandes protocolos descentralizados se suele constituir una DAO a la que se le pasa el control de los contratos y ya será esta quien gobernada por la comunidad tomaŕa las decisiones sobre los contratos. Miremos el ejemplo de UniSwap uno de los mayores DEX (Exchange Descentralizado) que existen, su funcionamiento se divide en tres:

1. El exchange: que sirve para hacer swapping entre diferentes tokens y aportar liquidez.

Figura 3: Página de Swapping de UniSwap entre tokens

2. El sistema de gobernanza del protocolo: un conjunto de SmartContracts que junto con el token $UNI conforman el control del protocolo. El funcionamiento de este sistema de gobernanza es bastante complejo y daría para un artículo bastante largo, por lo que podemos dejarlo en que se vota y propone usando $UNI.

Figura 4: Web app de gobernanza de UniSwap

3. La DAO de la comunidad de Uniswap: aunque la mayoría de las acciones que se hacen son off-chain todo el sistema de proponer cambios se hace a través del sistema de votación on-chain.

Esta es una de las maneras en las que se puede mantener la “descentralización” en un protocolo pero hay otras muchas con sus ventajas e inconvenientes pero lo que sí que podéis tener claro es que si veis alguno que no tiene sistema de gobernanza pero sus SmartContracts son actualizables huid de él porque en cualquier momento pueden robar los fondos, manipular los datos…

¿Cómo implementan los proxies los protocolos y DAOs?

Al final el patrón proxy es el patrón más usado para actualizar la lógica de los SmartContracts, implementarlo a mano puede ser tedioso y complejo dado que hay que tener en cuenta cosas como las colisiones de almacenamiento…(en el anterior artículo lo tratamos en mayor profundidad) y por ello es que se han creado herramientas y librerías que nos facilitan la vida a los desarrolladores solucionando en parte problemas de los que hablamos antes.
 
Os voy a mostrar dos formas de hacerlo; una fácil desde el punto de vista de implementación que solo requerirá de un par de cambios en nuestro SmartContract y otra más complicada que exigirá más cambios. Os voy a mostrar las dos porque la primera de ellas nos va a dar un control muy vago de lo que está sucediendo y la última nos permite visualizar de manera más clara lo que estamos haciendo.

1.- Forma “fácil”:

Esta manera es “sencilla” de cara a que no tenemos que hacer nada más que cambiar un par de variables, todo nos lo va ha hacer un módulo de Openzeppelin montado sobre la herramienta de Hardhat, veamos un ejemplo simple.

Figura 5: Proyecto de Solidity usando Hardhat

Para el ejemplo crearemos un proyecto nuevo de JavaScript con Yarn, Npm, Pnpm… u otro gestor de paquetes de NodeJs, e instalamos las siguientes dependencias, dentro de la consola:
“””
yarn init -y
yarn add hardhat @openzeppelin/contracts @openzeppelin/hardhat-upgrades
yarn hardhat
“”
Ahora seleccionaremos las siguientes opciones en la consola:

Figura 6: Selección de opciones en HardHat
  • “Create a JavaScript project”
  • Le damos a “y” a todo lo que nos pregunte HardHat
A continuación añadimos el siguiente contrato que nos servirá de prueba dentro de la carpeta “contracts”.
“
pragma solidity ^0.8.0;

contract Box {
    uint256 private _value;
    event ValueChanged(uint256 value);

    function store(uint256 value) public {
        _value = value;
        emit ValueChanged(value);
    }

    function retrieve() public view returns (uint256) {
        return _value;
    }
}
“
A continuación creamos un script de JavaScript para desplegar el contrato, vamos a modificar un poco el típico script que nos da Hardhat por defecto, dentro del ejemplo que nos da Hardhat escribimos lo siguiente:
“
const { ethers, upgrades } = require("hardhat");

async function main() {
  const Box = await ethers.getContractFactory("Box");
  console.log("Deploying Box...");
  const box = await upgrades.deployProxy(Box, [42], { initializer: "store" });
  await box.deployed();
  console.log("Box deployed to:", box.address);
}

main();
“
Lanzamos el siguiente comando y copiamos el address que nos muestra la consola.
“
yarn hardhat node (en una nueva consola)
yarn hardhat run [nombreScritp].js --network localhost
”
Ahora tenemos el contrato desplegado mediante un Proxy sin que tengamos que haber hecho nada especial más que instalar un par de librerías, guay eh, pues ahora vamos a actualizar mediante ese Proxy el contrato, para ello creamos una nueva versión del SmartContract y del Script. Al nuevo SmartContract lo podemos llamar “Box2” ya que es la segunda versión y este ahora más que guardar un solo valor le vamos a permitir cambiar ese valor incrementándolo al llamar a una función “storeIncrement”.
“
pragma solidity ^0.8.0;
contract Box2{
    uint256 private _value;
    uint256 private _increments;
    event ValueChanged(uint256 value);
    function store(uint256 value) public {
        _value = value;
        emit ValueChanged(value);
    }
    function storeIncrement(uint256 value) public {
        _increments = value;
    }
    function retrieveIncrement() public view returns (uint256) {
        return _increments;
    }
    function retrieve() public view returns (uint256) {
        return _value;
    }
    function increment() public {
        _value = _value + 1;
        emit ValueChanged(_value);
    }
}
”
Y dentro del script nuevo insertamos:
“
const { ethers, upgrades } = require("hardhat");

async function main() {
  const BoxV2 = await ethers.getContractFactory("Box2");
  console.log("Upgrading Box...");
  await upgrades.upgradeProxy(
    "[addressContratoDesplegado]",
    BoxV2
  );
  console.log("Box upgraded");
}

main();
”
Simplemente tenemos que lanzar el siguiente comando y ya tendremos actualizado nuestro SmartContract.
“
yarn hardhat run upgrade_box.js --network localhost
”
Como veis es una forma relativamente sencilla de actualizar un SmartContract sin tener que cambiar nada de este y sin siquiera saber que es un Proxy en la EVM, pero esto lleva consigo unos contras como por ejemplo no llevar el control real del “layout” del almacenamiento del contrato lo que puede ser catastrófico a la hora de actualizar el contrato a una nueva versión.

2.- Forma difícil:

La anterior herramienta nos ofrece muchas facilidades para actualizar nuestros SmartContracts sin embargo en grandes proyectos necesitamos tener un mayor control y conocimiento sobre el SmartContract y no usar una herramienta que “hace magia” para actualizar los SmartContractsLa solución es utilizar librerías de Solidity que solo añaden código y no modifican nuestro SmartContract. Para después ser nosotros quien realice el despliegue y las actualizaciones para saber en todo momento qué es lo que ocurre.

Figura 7: Modelo Proxy en Smart Contracts

Recordemos que a la hora de actualizar SmartContracts mediante proxies tenemos que tener en cuenta que el contrato que despleguemos va a ser solo lógica y que esta va a interactuar para modificar el almacenamiento de otro contrato, visualmente se vería como la imagen de abajo. Si por ejemplo tuviéramos un SmartContract como el siguiente
“
pragma solidity ^0.8.0;

contract Box {
    uint256 private _value = 54;
// … rest go above
}
“
Podríamos observar que configuramos una variable con un valor por defecto. Lo que hace el compilador a la hora de transformar nuestro SmartContract en Bytecode es añadir una función, llamémosla “firstInit”, que se ejecutará al subir este contrato a la cadena de Blockchain actualizando el valor de las variables al que hayamos especificado, lo mismo sucede con las variables en las que definamos un valor en el constructor.

Ahora os pregunto, imaginando que ya tenemos una versión v1 de nuestro SmartContract desplegado mediante un proxy, si al actualizar el SmartContract (de la lógica, es decir, éste) subiendo uno nuevo que añade otros valores en el constructor, ¿estos nuevos valores tendrán efecto en el proxy que guarda todo el almacenamiento o en el contrato de la lógica en el que no sirve de nada guardar valores dado que estos no se van a usar?

La respuesta es la segunda, estos valores se guardarán en el contrato de la lógica lo que no tiene sentido hacer porque entonces nunca usaremos estas variables, es por ello que a la hora de escribir SmartContracts que van a desplegarse mediante proxy hay que eliminar todas las variables que iniciemos en el constructor o en el cuerpo del contrato. Podemos crear una función que actualice esos valores y llamarla a través del proxy una vez se haya actualizado el contrato para que este tenga efecto:
“
pragma solidity ^0.8.0;

contract Box {
    uint256 private _value;
    function initialize(){
	private_value = 54;
   }
}
“
Y lo mismo tenemos que hacer con los contratos grandes. Si usamos estándares ya definidos como el ERC20 o el ERC721 ya existen versiones de estos con los cambios necesarios para actualizarlos mediante proxies, las librerías de Openzeppelin son un buen punto de partida. Si queréis ver cómo se implementan estás aquí os dejo un tutorial.

Conclusión  

Como podéis ver,  el patrón proxy es un patrón complejo que aporta mucha utilidad, pero que para poder mantener esa descentralización es necesario que una comunidad respalde por detrás el proyecto. Nos vemos en los siguientes artículos, pero tienes muchos más artículos sobre este mundo Web3 aquí mismo:
Saludos,

AutorChema Garabito. Desarrollador Full-Stack. Ideas Locas Telefónica CDO.

lunes, julio 18, 2022

Blockchain & SmartContracts: Actualizar SmartContracts con Patrones Proxy

Siempre que hablamos de SmartContracts decimos que es muy importante tener presente que una vez subas tu código a la Blockchain, éste se quedará así para siempre y si tiene un bug o vulnerabilidad pues lo tendrá permanentemente. Pero y si tuviéramos algún tipo de patrón mediante el cual pudiéramos actualizar la lógica de nuestro contrato, entonces no tendríamos que pedir a nuestros usuarios que cambien el contrato que usaban, simplemente podemos actualizarlo y ya. 

Figura 1: Blockchain & SmartContracts.
Actualizar SmartContracts con Patrones Proxy

Si bien existen diferentes formas en las que podemos “actualizar” la lógica de un SmartContract como por ejemplo usando un modelo de “módulos arbitrarios” aprovechándose de que estos contratos pueden ejecutar código de manera arbitraría. Pero el patrón más usado es el patrón “Proxy”.

Figura 2: Idea general del patrón Proxy

Pero hoy nos vamos a centrar en los Proxies, que son un patrón que nos permite actualizar completamente la lógica de nuestros contratos usando solamente dos contratos diferentes, cabe destacar que estos dos contratos son inmutables, pero combinándolos podremos actualizar su funcionamiento. La idea básica de su funcionamiento es la siguiente:

Figura 3: Despliegue de SmartContract Proxy

Tenemos dos contratos diferentes, el primero el Proxy o Storage que se encargará de guardar en la Blockchain todos los datos del SmartContract. Este contrato siempre será el mismo y nunca cambiará, esto es así dado que mover los datos de un contrato a otro es muy costoso. El segundo el contrato de la lógica, como su nombre nos indica este se encargará de manejar la lógica por la que se regirá el SmartContract, este irá cambiando con el tiempo oséa que crearemos nuevos contratos y cambiaremos éste por los nuevos.

Figura 4: Actualizando contrato Proxy

Ahora, en cada interacción que el usuario haga la hará con el contrato Storage y esté a su vez le pasará la llamada al contrato que guarda la lógica y éste modificará la memoria del contrato Storage y no la suya. Ahora imaginemos que queremos actualizar el contrato que utiliza el usuario, pues simplemente subimos un nuevo contrato a la Blockchain que tenga esta lógica nueva y cambiamos en el contrato storage el puntero que redirigía al contrato anterior por el nuevo.

Funcionamiento del Patrón Proxy

Acabamos de ver la idea general y básica sobre cómo funcionan los sistemas proxies en los contratos basados en la EVM, veamos ahora cómo convertir esta idea en algo real. En la EVM (Ethereum Virtual Machine) existe una instrucción llamada delegatecall que funciona de la siguiente manera.
“””
delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
“””
Cabe notar que en Ethereum las instrucciones de código ensamblador se usan como si fueran funciones y no como “ add bx, 5; “ usado por la mayoría de lenguajes de ensamblador. Esta instrucción nos permite llamar a un contrato externo, igual que la instrucción “ call “. Bien pero, ¿qué diferencia hay entre estas instrucciones? Pues que delegatecall no solamente llama a la función de un contrato externo sino que además pasa todo el contenido guardado en msg.data (parámetros de llamada) y msg.value (ETH enviado).

Además teniendo la peculiaridad de que todas las modificaciones que haga el contrato al que se le delega la llamada no se reflejará en el propio contrato sino que lo hará en aquel que haya ejecutado delegatecall. La instrucción delegatecall recibe cinco parámetros:
  • gas: El gas del que va a disponer el contrato al que se le delega la llamada. No siempre este es el máximo posible porque a veces antes de delegar la llamada primero evaluamos unas ciertas condiciones que gastan el gas inicial, por ello es importante calcularlo adecuadamente.
  • _impl: El address del contrato hacia el cual delegamos la llamada. En el patrón proxy se le suele llamar implementación.
  • prt: Los datos del msg.data que se pasan al nuevo contrato, puede bastar con pasar msg.data en vez de ptr, pero puede que primero necesitemos procesar los datos de entrada. El msg.data acabará convirtiendose en los parámetros que le lleguen al contrato final.
  • calldatasize: El valor en bytes de la longitud de memoria que ocupa el msg.data.
  • últimos 2 parámetros: Estos dos últimos parámetros no son importantes, solo sirven para que el valor devuelto por el contrato al que se delegue sea acotado entre el rango provisto en esos dos valores.
Cabe destacar que si usamos la función addr.delegatecall(...) en Solidity, ésta sólo nos devolverá un booleano que nos dirá si la función ha fallado o no. Sin embargo si usamos la instrucción de ensamblador podremos acceder a los datos que devuelva el contrato al que se delegue usando un pequeño truco que veremos más adelante.

Implementación en Solidity

Toda esta teoría está muy bien pero veamos cómo podemos implementar todo esto en Solidity. Para ello vamos ha hacer uso de él “inline-assembly” que nos ofrece Solidity para así poder escribir ensamblador dentro del propio código de Solidity.

Figura 5: Función fallback en un Proxy

Antes de explicar que ocurre dentro de la función veamos el contexto de porque esa función. Si nos fijamos bien veremos que no estamos definiendo una función normal, sino una “fallback” esta función tiene un rol muy importante dentro de los SmartContracts basados en EVM, ésta se ejecutará siempre que se llame a un contrato y la función a la que se está llamando no exista en el propio contrato. Si queréis entender en profundidad cómo funciona este sistema de llamadas os recomiendo leer este artículo sobre los selectores de las funciones en la EVM.

Explicacion del codigo:

Lo primero que hacemos es reservar en memoria un espacio en el que poder guardar los datos que se envían junto con la función como parámetros, etcétera. Y ahora os preguntaréis ¿Y cómo accedemos a ellos si no están declarados en la función? Pues en la EVM todos los datos que se envían a una función están guardados dentro de una variable global llamada calldata. Después copiamos al espacio reservado en memoria los datos guardados en calldata. Esto es necesario ya que calldata es un tipo de memoria que no se puede ni modificar ni pasar a otras funciones como parámetros.

Luego delegamos la llamada a un address llamado _impl y le pasamos el calldata que escribimos en memoria, delegatecall nos devolverá un valor boolean que debemos guardar. Y también guardamos en memoria la longitud de la respuesta del delegatecall. Una vez hecho eso, copiamos el espacio reservado en 0 los valores devueltos por 2 y evaluamos si la llamada delegada fue exitosa o no. En caso de que no devolvemos el error que nos tiró el contrato de lógica, sino devolvemos los valores.

Problemas que pueden surgir

Una cosa muy importante a tener en cuenta es cómo se guarda el nuevo almacenamiento que creamos al añadir lógica a un contrato. Veamos primero cómo transforma Solidity su código a instrucciones.
“””
contract storage{
	uint256 age = 12;// transforms to sstore(0x00,0xC)
	uint256 other = 1; // transforms to sstore (0x40, 0x1)

	function modify() external{
		age = 13; // transforms to sstore(0x00,0xD)
		other = 2; // transforms to sstore(0x40, 0x2)
              }
}
“””
A la hora de actualizar este contrato tenemos que tener mucho cuidado de no hacer override de estas variables o podríamos tener un problema bastante serio, miremos el siguiente ejemplo para ver a lo que me refiero.
“””
contract storageV2{
uint256 score = 100; // transforms to sstore(0x00,0x64)
	uint256 age; // setted in V1 as 0x00 but not is 0x40
	uint256 other; // we setted in V1 0x40 but now is in position 0x60

	function modify() external{
		age = 13; // now it stores age in other slot
		other = 2; // now it stores other in a new slot
		score = 75; // stores it in the age slot
              }
}
“””
Este contrato que trata de actualizar la lógica del contrato anterior está mal, porque lleva a una colisión de almacenamiento. Esto es que cuando tratemos de leer lo que había en age v1 estaremos leyendo lo que había en otro v1 y si leemos other obtendremos 0 dado que no hay datos guardados en el slot nuevo. De manera visual la colisión sería la siguiente:

Figura 6: Storage Collision

Y los datos los podemos seguir leyendo porque aunque estén cambiados a fin de cuentas un uint256 es 32bytes con lo que se lee de la misma manera, pero si estuviésemos leyendo un mapping o un string los datos que leeremos no tendrían siquiera sentido alguno. También existen otros tipos de problemas como el de la transparencia para el usuario, control de acceso y actualización. Estos se resuelven de manera un poco más compleja.

¿Descentralización y transparencia?

Es verdad que es muy útil el poder actualizar la lógica de tus contratos por si acaso algún día se encuentra una vulnerabilidad en tu código o simplemente necesitas extender su funcionalidad, pero esto rompe un poco con los principios de inmutabilidad y transparencia. ¿Pero es esto una carencia? ¿De verdad rompe con la descentralización? Os voy adelantando que no, existen mecanismos para mantener descentralizada la propiedad del contrato y sus actualizaciones, pero esto no lo veremos hoy.


En el siguiente artículo os mostraré algunos problemas más comunes a la hora de usar Proxies en SmartContracts y como está el “state of the art” en lo relacionado a proxies en desarrollo Blockchain y como podéis implementarlo vosotros en vuestros proyectos. Nos vemos en el próximo artículo. Más artículos sobre este mundo Web3:
Saludos,

AutorChema Garabito. Desarrollador Full-Stack. Ideas Locas Telefónica CDO.


Entrada destacada

Hacking IA: Jailbreak, Prompt Injection, Hallucinations & Unalignment. Nuestro nuevo libro en 0xWord

Pocas veces me ha hecho tanta ilusión que saliera un nuevo libro en 0xWord como con este libro de " Hacking IA: Jailbreak, Prompt Inje...

Entradas populares