highCWE-362A04:2021

Condicion de Carrera en Pagos

Operaciones de lectura-modificacion-escritura en pagos sin transacciones de base de datos permiten a los atacantes explotar ventanas de tiempo y gastar el mismo saldo multiples veces.

Cómo Funciona

Una condicion de carrera en pagos ocurre cuando la aplicacion lee el saldo del usuario, verifica si es suficiente y luego deduce el monto en operaciones separadas no atomicas. Si un atacante envia multiples requests simultaneos, todos pueden leer el saldo original antes de que se escriba cualquier deduccion. Cada request ve fondos suficientes y procede con la compra. Por ejemplo, un usuario con $100 envia 10 requests concurrentes para un producto de $50. Los 10 leen $100, los 10 aprueban la compra, resultando en $500 en productos por $100. Esto se llama vulnerabilidad TOCTOU (Time Of Check to Time Of Use) y es comun en e-commerce, monedas in-app y sistemas de wallet.

Código Vulnerable
app.post('/api/purchase', async (req, res) => {
  const user = await db.user.findUnique({ where: { id: req.userId } });
  if (user.balance < req.body.amount) {
    return res.status(400).json({ error: 'Insufficient funds' });
  }
  await db.user.update({ where: { id: req.userId },
    data: { balance: user.balance - req.body.amount } });
  await db.order.create({ data: { userId: req.userId, amount: req.body.amount } });
  res.json({ success: true });
});
Código Seguro
app.post('/api/purchase', async (req, res) => {
  const result = await db.$transaction(async (tx) => {
    const user = await tx.user.findUnique({ where: { id: req.userId } });
    if (user.balance < req.body.amount) throw new Error('Insufficient funds');
    const updated = await tx.user.update({ where: { id: req.userId },
      data: { balance: { decrement: req.body.amount } } });
    if (updated.balance < 0) throw new Error('Insufficient funds');
    return tx.order.create({ data: { userId: req.userId, amount: req.body.amount } });
  });
  res.json({ success: true });
});

Ejemplo Real

En 2018, Starbucks revelo una condicion de carrera en su sistema de tarjetas de regalo que permitia a los atacantes duplicar saldos haciendo requests de transferencia simultaneos entre tarjetas. El bug fue descubierto a traves de su programa de bug bounty y habia sido explotado para generar credito ilimitado en tiendas.

Cómo Prevenirlo

  • Envuelve todas las operaciones de lectura-verificacion-actualizacion de saldo en una transaccion de base de datos
  • Usa operaciones atomicas como decrement en vez de leer y restar
  • Agrega bloqueo optimista con columnas de version para detectar modificaciones concurrentes
  • Implementa llaves de idempotencia para prevenir procesamiento duplicado del mismo request

Tecnologías Afectadas

Node.jsReactNext.jsPythonGoJavaPHP

Data Hogo detecta esta vulnerabilidad automáticamente.

Escanea Tu Repo Gratis

Vulnerabilidades Relacionadas