Seguridad
Lineamientos de seguridad OWASP Top 10, validación de input y headers HTTP para aplicaciones del DTI.
Seguridad — Estándares de seguridad para todas las aplicaciones del DTI
Propósito
Esta guía establece los requisitos de seguridad mínimos para todas las aplicaciones desarrolladas por el DTI. El objetivo es proteger los sistemas y los datos de los usuarios.
OWASP Top 10
A01: Broken Access Control
El acceso a recursos debe ser verificado en cada request.
# FastAPI - verificar permisos en endpoint
from fastapi import Depends, HTTPException
from app.core.security import get_current_user
@router.get("/users/{user_id}")
def get_user(user_id: int, current_user = Depends(get_current_user)):
if current_user.id != user_id and current_user.role != "admin":
raise HTTPException(403, "No tienes acceso a este recurso")
return {"user_id": user_id}
// NestJS - usar Guards
@UseGuards(RolesGuard)
@Roles('admin')
@Get('users/:id')
getUser(@Param('id') id: string) {}
Requerimientos:
- No confiar solo en client-side validation
- Deny por defecto: recursos no públicos deben requerir auth
- Verificar ownership de recursos antes de retornar datos
A02: Cryptographic Failures
Nunca almacenar passwords en texto plano.
# Django - usar hash seguro
from django.contrib.auth.hashers import make_password, check_password
# Crear usuario
user.password = make_password(raw_password)
# Verificar password
check_password(raw_password, stored_hash)
// Node.js - usar bcrypt
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 12);
const match = await bcrypt.compare(password, hash);
Requerimientos:
- Usar bcrypt, argon2 o scrypt para passwords
- No usar MD5 o SHA-1 para passwords
- Almacenar solo el hash, nunca el password real
A03: Injection
Validar y sanitizar todo input de usuario.
# ✗ INCORRECTO - SQL Injection vulnerable
query = f"SELECT * FROM users WHERE email = '{email}'"
# ✓ CORRECTO - Usar queries parametrizadas
from sqlalchemy import text
query = text("SELECT * FROM users WHERE email = :email")
result = db.execute(query, {"email": email})
// ✗ INCORRECTO
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✓ CORRECTO - Usar ORM o queries parametrizadas
const user = await prisma.user.findUnique({
where: { id: userId }
});
Requerimientos:
- Usar ORM (SQLAlchemy, Prisma, TypeORM) para todas las queries
- Si se usa SQL raw, usar placeholders :name
- Validar tipos: ints deben ser ints, emails deben ser emails
A04: Insecure Design
Realizar threat modeling en la fase de diseño.
## Threat Modeling - Nuevo endpoint
### Asset: Datos de estudiantes
### Threats:
1. Acceso no autorizado a registros
2. Modificación de notas por usuarios no autorizados
3. Exposición de datos personales
### Mitigaciones:
- Verificar rol antes de retornar datos
- Logs de auditoría para modificaciones
- Encriptación en tránsito (TLS)
A05: Security Misconfiguration
Usar configuración segura por defecto.
# FastAPI - configuración de seguridad
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["api.unach.cl"]
)
Requerimientos:
- Deshabilitar debug en producción
- Usar headers de seguridad (ver sección abajo)
- Remover información de version en respuestas
A06: Vulnerable Components
Mantener dependencias actualizadas.
# Ver dependencias con vulnerabilidades
pip audit
npm audit
Requerimientos:
- Revisar dependencias mensualmente
- Usar herramienta de scanning en CI/CD
- Aplicar parches de seguridad rápidamente
A07: Authentication Failures
Usar SSO institucional, no implementar auth custom.
// Google OAuth - verificar dominio
const verifyEmailDomain = (email: string): boolean => {
return email.endsWith('@unach.cl');
};
A08: Software and Data Integrity
Verificar integridad de dependencias.
# .gitlab-ci.yml
dependencias:
script:
- npm audit --audit-level=high
- pip audit
A09: Security Logging
Loggear eventos de seguridad.
import logging
security_logger = logging.getLogger("security")
# Login exitoso
security_logger.info(
"Login exitoso",
extra={"user_id": user.id, "ip": client_ip}
)
# Login fallido
security_logger.warning(
"Login fallido",
extra={"email": email, "ip": client_ip, "attempts": 3}
)
A10: SSRF
Validar URLs antes de hacer requests externos.
from urllib.parse import urlparse
def is_safe_url(url: str) -> bool:
parsed = urlparse(url)
# Bloquear localhost y redes privadas
blocked = ['127.0.0.1', 'localhost', '0.0.0.0', '169.254.169.254']
return not any(net in parsed.hostname for net in blocked)
Headers de seguridad HTTP
Agregar en todas las respuestas HTTP:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
# FastAPI - middleware de seguridad
@app.middleware("http")
async def security_headers(request, call_next):
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
return response
Checklist de seguridad para code review
- ¿Se validan todos los inputs de usuario?
- ¿Se usa ORM o queries parametrizadas?
- ¿Los passwords están hasheados con bcrypt/argon2?
- ¿Se verifican permisos en cada endpoint?
- ¿Se usan headers de seguridad?
- ¿Se loggean eventos de seguridad (login, cambios)?
- ¿No hay credenciales hardcodeadas?
- ¿Se validan URLs antes de hacer requests externos?
- ¿Las variables de entorno están en .env, no en código?
- ¿El dominio de email se verifica para SSO?