Skip to content

Security Misconfiguration

CWE: CWE-16 - Configuration
Problema: Configuración deficiente de seguridad


Tipos de Misconfiguration

1. Default Credentials

bash
# Credenciales por defecto en producción
curl -u admin:admin https://api.app.com/api/admin
curl -u root:root https://api.app.com/api/admin
curl -u test:test https://api.app.com/api/admin

# Acceso exitoso = Misconfiguration

2. CORS Débil

bash
# CORS permite cualquier origen
curl "https://api.app.com/api/data" \
  -H "Origin: https://attacker.com"

# Respuesta:
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Credentials: true  ← PELIGROSO

# Atacante accede desde su sitio:
fetch('https://api.app.com/api/data', {
  method: 'GET',
  credentials: 'include'
})

3. HTTP en lugar de HTTPS

bash
# API accesible sin encriptación
curl http://api.app.com/api/data
# Devuelve datos en texto plano

# Man-in-the-middle intercepta credenciales

4. Debug Mode Habilitado

bash
# Modo debug en producción
GET https://api.app.com/api/debug

# Respuesta:
{
  "debug": true,
  "database_url": "postgres://user:pass@db:5432/prod",
  "api_keys": [...],
  "stack_trace": "..."
}

# Stack traces revelan estructura interna

5. Errores Detallados

bash
# API retorna información en errores
curl "https://api.app.com/api/users/999" \
  -H "Authorization: Bearer invalid_token"

# Respuesta:
{
  "error": "Token verification failed",
  "details": "JWT secret is: 'super_secret_key_12345'",
  "hint": "Check /config/.env for more info",
  "database_error": "Connection to 192.168.1.100:5432 refused"
}

6. Información en Headers

bash
curl -v https://api.app.com/api/data

# Response headers:
X-Powered-By: Express 4.17.1 Versión
X-AspNet-Version: 4.0.30319 Framework
Server: Apache/2.4.1 Versión servidor
X-Debug-Token: debugging_enabled
X-API-Version: v2.1.0 Versión API

7. Archivos Accesibles

bash
# Archivos de configuración
.env
.env.backup
config.json
config.php
database.yml
secrets.yaml

# Archivos administrativos
.git/
.gitignore
.env.production
docker-compose.yml
Dockerfile
requirements.txt

# Acceso directo
curl https://api.app.com/.env
curl https://api.app.com/config.json
curl https://api.app.com/.git/HEAD

8. API Versiones Sin Actualizar

bash
# Versiones antiguas aún funcionales
GET /api/v1/users       # Versión 1 sin validación
GET /api/v2/users       # Versión 2 con validación
GET /api/v3/users       # Versión 3 corriente

# Atacante usa v1 que no tiene protecciones
curl "https://api.app.com/api/v1/users/admin/password" \
  -d '{"new_password": "hacked"}'

Explotación

python
import requests
import json

def check_misconfigurations(api_url):
    """
    Verificar errores de configuración
    """
    
    checks = [
        {
            "name": "Default credentials",
            "url": f"{api_url}/api/admin",
            "auth": ("admin", "admin"),
            "check": lambda r: r.status_code == 200
        },
        {
            "name": "CORS misconfiguration",
            "url": f"{api_url}/api/data",
            "headers": {"Origin": "https://attacker.com"},
            "check": lambda r: "*" in r.headers.get("Access-Control-Allow-Origin", "")
        },
        {
            "name": ".env accessible",
            "url": f"{api_url}/.env",
            "check": lambda r: r.status_code == 200 and "API_KEY" in r.text
        },
        {
            "name": ".git accessible",
            "url": f"{api_url}/.git/HEAD",
            "check": lambda r: r.status_code == 200
        },
        {
            "name": "Debug mode enabled",
            "url": f"{api_url}/api/debug",
            "check": lambda r: "debug" in r.text.lower()
        },
        {
            "name": "Detailed error messages",
            "url": f"{api_url}/api/invalid",
            "check": lambda r: "stack" in r.text.lower() or "database" in r.text.lower()
        }
    ]
    
    for check in checks:
        try:
            if "auth" in check:
                r = requests.get(check["url"], auth=check["auth"], timeout=5)
            else:
                headers = check.get("headers", {})
                r = requests.get(check["url"], headers=headers, timeout=5)
            
            if check["check"](r):
                print(f"[✓] MISCONFIGURATION: {check['name']}")
        except:
            pass

# Ejecutar
check_misconfigurations("https://api.app.com")

Mitigación

1. CORS Restrictivo

python
from flask_cors import CORS

# ✗ NUNCA hacer esto
CORS(app)  # Permite todos los orígenes

# ✓ Whitelist de orígenes
CORS(app, resources={
    r"/api/*": {
        "origins": ["https://trusted-domain.com"],
        "methods": ["GET", "POST"],
        "allow_headers": ["Content-Type", "Authorization"],
        "supports_credentials": True,
        "max_age": 3600
    }
})

2. Headers de Seguridad

python
@app.after_request
def set_security_headers(response):
    # Ocultar información del servidor
    response.headers['Server'] = 'WebServer'
    
    # Deshabilitar CORS genérico
    response.headers['Access-Control-Allow-Origin'] = 'https://trusted.com'
    
    # HSTS (HTTPS obligatorio)
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    
    # Prevenir clickjacking
    response.headers['X-Frame-Options'] = 'DENY'
    
    # Prevenir MIME sniffing
    response.headers['X-Content-Type-Options'] = 'nosniff'
    
    # CSP
    response.headers['Content-Security-Policy'] = "default-src 'self'"
    
    return response

3. Remover Información Sensible

python
@app.errorhandler(Exception)
def handle_error(error):
    """
    No retornar stack traces en producción
    """
    
    # Logging interno
    logger.error(f"Error: {str(error)}", exc_info=True)
    
    # Respuesta al cliente
    if app.config['DEBUG']:
        return {"error": str(error), "traceback": traceback.format_exc()}, 500
    else:
        return {
            "error": "Internal server error",
            "request_id": "xxx-yyy-zzz"  # Para debugging
        }, 500

4. Proteger Archivos Sensibles

nginx
# Nginx - Bloquear acceso
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

location ~ /\.(env|git|env\.) {
    deny all;
}

location ~ /(config|secrets|keys|credentials) {
    deny all;
}

5. HTTPS Obligatorio

python
@app.before_request
def enforce_https():
    if not request.is_secure and app.config['ENV'] == 'production':
        url = request.url.replace('http://', 'https://', 1)
        return redirect(url, code=301)

6. Deshabilitar Debug en Producción

python
# ✓ Correcto
app.config['DEBUG'] = False
app.config['TESTING'] = False

# ✗ Nunca hacer en producción
app.config['DEBUG'] = True

# Verificar
if app.config['DEBUG']:
    raise RuntimeError("DEBUG MODE IS ENABLED IN PRODUCTION!")

Checklist

  • [ ] HTTPS en todos los endpoints
  • [ ] CORS configurado restrictivamente
  • [ ] Debug mode deshabilitado
  • [ ] Errores sin stack traces
  • [ ] Headers de seguridad configurados
  • [ ] Sin credenciales por defecto
  • [ ] Archivos .env/.git no accesibles
  • [ ] Versiones antiguas de API removidas
  • [ ] Información del servidor oculta