> For the complete documentation index, see [llms.txt](https://4lec4st.gitbook.io/4lec4st/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://4lec4st.gitbook.io/4lec4st/pt1-tryhackme/web-hacking/race-condition.md).

# Race Condition

### 📚 Programa

Un **programa** es un conjunto de instrucciones diseñadas para cumplir una tarea específica. Hasta que no lo ejecutes, no hace nada; permanece como código estático.

Imagina una receta de café que descargaste con cardamomo y canela. Estas son las instrucciones:

1. Mezcla café preparado, cardamomo, canela y clavos (si decides usarlos) en una cacerola.
2. Calienta a fuego lento durante 5 minutos, removiendo ocasionalmente. No hierbas.
3. Cuela el café en tu taza.
4. Añade leche o endulza al gusto.

Si nadie sigue esos pasos… ¡no se sirve café!

Lo mismo ocurre con este ejemplo mínimo en Flask (Python); define un servidor que escucha en el puerto 8080 y responde con “Hello, World!”. Pero si no ejecutas ese código, no obtendrás ninguna página:

```python
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return '<html><head><title>Greeting</title></head><body><h1>Hello, World!</h1></body></html>'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
```

***

### 🔄 Proceso

Estás en **proceso** cuando ejecutas la receta paso a paso. Puedes recibir una llamada urgente o realizar otra tarea mientras esperas que hierva el agua. Estas interrupciones son inevitables. Lo mismo ocurre con un **proceso**: ejecución dinámica de un programa.

Un **proceso** (o job) es una entidad dinámica que contiene:

* **Programa**: el código ejecutable.
* **Memoria**: almacenamiento temporal de datos.
* **Estado**: puede estar en varios estados – New → Ready → Running → Waiting → Terminated.

Por ejemplo, si ejecutas el servidor Flask, el proceso se mantiene en estado **Waiting** (a la espera de conexiones). Al recibir una petición HTTP, pasa a **Ready**, luego a **Running** para procesarla, y vuelve a **Waiting**.

Con `flask run --without-threads`, el servidor atiende peticiones **secuencialmente**, una por una.

***

### 🧵 Hilo (Thread)

Volvamos al café: una máquina de expreso con dos grupos (portafiltros). Cuando llega un pedido, se usa un portafiltro. Si otro pedido llega y hay uno libre, se atiende al instante.

* La **máquina encendida** = proceso.
* Cada **portafiltro** = hilo.

Un **hilo** es una unidad de ejecución ligera que **comparte memoria e instrucciones** con su proceso.

#### 💡 Procesamiento: solo vs multihilo

* **Serie**: un proceso atiende usuarios uno tras otro.
* **Paralelo**: un proceso crea un hilo por usuario hasta un límite.

Ejemplo con Gunicorn (un servidor WSGI para Python):

```bash
gunicorn --workers=4 --threads=2 -b 0.0.0.0:8080 app:app
```

Esto crea 4 **procesos** (workers), cada uno con 2 **hilos**. El puerto 8080 solo puede estar ocupado por un proceso, pero los hilos gestionarán las peticiones concurrentes.

***

### ⚠️ Condición de carrera (Race Condition)

Imagina que reservas la mesa 17 en un restaurante y otro cliente también lo hace casi al mismo tiempo:

* ¿Quién la reservó realmente?
* La demora en etiquetar la mesa provocó un **race condition**.

Lo mismo ocurre cuando un hilo comprueba un valor y otro lo modifica antes de que actúe.

#### 🏦 Ejemplo A

* Saldo: $100.
* Hilo 1 retira $45 (consulta → $100).
* Hilo 2 retira $35 (también ve $100).
* Si Hilo 1 actualiza primero, saldo → $55. Luego Hilo 2 actualiza → $65… ¡incorrecto!

#### 🏦 Ejemplo B

* Saldo: $75.
* Hilo 1 retira $50 (consulta → $75).
* Hilo 2 retira $50 (consulta también $75).
* Así, la segunda retirada debería rechazarse, pero se realiza.

Ambos casos muestran vulnerabilidades TOCTOU (Time-of-Check to Time-of-Use).

***

### 🧩 Código de ejemplo

```python
import threading
import time

def increase_by_10():
    for i in range(1, 11):
        print(f"Thread {threading.current_thread().name}: {i}0% complete")

thread1 = threading.Thread(target=increase_by_10, name="Thread-1")
thread2 = threading.Thread(target=increase_by_10, name="Thread-2")

thread1.start()
thread2.start()
thread1.join()
thread2.join()

print("Both threads have finished completely.")
```

* No hay control del orden ni sincronización.
* Cada ejecución puede mostrar un orden distinto en la consola.

***

### 🛡️ Causas comunes y mitigación

Las condiciones de carrera surgen al **compartir recursos** entre hilos/procesos:

* **Ejecución paralela**: múltiples peticiones web accediendo al mismo dato sin control.
* **Operaciones en base de datos**: lectura-modificación-escritura concurrente produce incoherencias.
* **Bibliotecas o servicios de terceros** no preparados para concurrencia.

#### Técnicas de mitigación:

1. **Bloqueos**: permiten acceso exclusivo a recursos.
2. **Operaciones atómicas**: agrupaciones indivisibles de acciones.
3. **Transacciones DB**: agrupar operaciones para ejecutarse todas o ninguna.

***

### 🌐 Arquitectura Cliente‑Servidor y condiciones de carrera

#### Modelo Cliente‑Servidor

* **Cliente**: solicita servicios (por ejemplo, un navegador web).
* **Servidor**: responde y entrega recursos (HTML, datos…).

#### Arquitectura multinivel (típica en web)

1. **Presentación**: navegador que interpreta HTML/CSS/JS.
2. **Aplicación**: lógica de negocio (Node.js, PHP…).
3. **Datos**: base de datos (MySQL, PostgreSQL…).

#### Estados en lógica de negocio

**1. Transferencia de dinero**

* Usuario confirma → consulta saldo → DB responde → si hay fondos, se procesa → devuelve resultado.

**2. Cupón de descuento**

* Usuario ingresa código → se consulta validez y restricciones → se aplica o rechaza.

Aunque parezcan dos estados (aplicado / no aplicado), en realidad hay varios:

* Validando → comprobando restricciones → aplicando → recalculando total…

Durante esos pasos, existe una **ventana temporal** en que se puede volver a aplicar el descuento o realizar una transferencia duplicada si los eventos son concurrentes.

***

### 🕵️ Detección y mitigación en entornos reales

* Detectar condiciones de carrera es complicado; puede pasar desapercibido sin monitoreo activo.
* Es esencial contar con pruebas de seguridad (penetration testing), bug bounty, herramientas como **Burp Suite Repeater**.
* Técnicas clave:
  * Sincronización y bloqueos
  * Operaciones atómicas
  * Transacciones @ DB
