Wikidelar es una plataforma open source de uso nacional. Este documento define el modelo de seguridad, los controles implementados, y las guías de operación segura para deployments propios.
| Actor | Nivel de confianza | Descripción |
|---|---|---|
| Visitante anónimo | Ninguno | Puede leer contenido público |
| Usuario registrado | Bajo | Puede contribuir, votar, comentar |
| Colaborador | Medio | Puede subir documentos y editar wikis |
| Editor | Medio-alto | Puede aprobar/rechazar contribuciones |
| Faculty Admin | Alto | Gestiona una facultad específica |
| Super Admin | Máximo | Acceso total al sistema |
- API REST pública (
/api/v1/*) — lectura sin auth, escritura con auth - Endpoints de administración (
/api/v1/admin/*) — solo admins - Upload de archivos — superficie especialmente sensible
- Importación EVA — solo accesible desde roles admin
- Algoritmo: HMAC-SHA256 (HS256)
- Duración: 60 minutos (configurable via
Jwt:ExpiryMinutes) - Claims incluidos:
sub(UserId),name,email,role - La clave
Jwt:Keydebe tener mínimo 32 caracteres y generarse con CSPRNG en producción
# Generar clave segura en Linux/Mac
openssl rand -base64 48
# PowerShell
[Convert]::ToBase64String((1..48 | ForEach-Object { [byte](Get-Random -Maximum 256) }))Nunca usar la clave del appsettings.Development.json en producción.
Configurada en Identity:
- Mínimo 8 caracteres
- Requiere mayúscula, minúscula, dígito y carácter especial
- Email único por usuario
SuperAdmin
└─ FacultyAdmin (por facultad)
└─ Editor
└─ Collaborator
└─ RegisteredUser
└─ (Anónimo — solo lectura)
Los endpoints de admin exigen [Authorize(Roles = "SuperAdmin,FacultyAdmin")].
Implementado con Microsoft.AspNetCore.RateLimiting (sliding window):
| Tipo de cliente | Límite | Ventana |
|---|---|---|
| Anónimo (por IP) | 100 req | 60 segundos |
| Autenticado | 200 req | 60 segundos |
Respuesta en exceso: HTTP 429 con header Retry-After.
En producción, considerar un rate limiter a nivel de infraestructura (nginx, Cloudflare) como primera línea.
El middleware SecurityHeadersMiddleware aplica en cada respuesta:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; script-src 'self'; ...
Para configurar CSP más permisivo (ej. si se embeben videos de YouTube), editar SecurityHeadersMiddleware.cs.
Configurar Cors:AllowedOrigins con los dominios exactos del frontend. No usar * en producción.
{
"Cors": {
"AllowedOrigins": ["https://wikidelar.edu.uy"]
}
}Todos los cambios a entidades persistidas se registran en la tabla AuditLogs. Ver AUDIT.md para detalles.
Aún no implementado en esta versión. Cuando se implemente, aplicar:
- Validación de tipo MIME en el servidor (no solo extensión)
- Límite de tamaño por archivo (recomendado: 10MB para documentos, 50MB máximo)
- Scan antivirus antes de almacenar (ClamAV o servicio externo)
- Almacenar en MinIO con bucket privado, nunca en filesystem del servidor
- Generar presigned URLs con expiración corta para descarga
- No preservar el nombre de archivo original en storage (usar UUID)
- Rechazar:
.exe,.sh,.bat,.php,.js,.htmly similares
EF Core con consultas parametrizadas protege contra SQL injection en queries normales. Al usar FromSqlRaw:
// MAL
context.Subjects.FromSqlRaw($"SELECT * FROM subjects WHERE name = '{name}'");
// BIEN
context.Subjects.FromSqlRaw("SELECT * FROM subjects WHERE name = {0}", name);En producción, nunca poner secretos en appsettings.json. Usar:
- Docker/Kubernetes: variables de entorno o Secrets
- Azure: Azure Key Vault + Managed Identity
- Bare metal:
dotnet user-secretsen dev, env vars en prod
Variables críticas:
ConnectionStrings__DefaultConnection
Jwt__Key
El proyecto es open source. Mantener dependencias actualizadas:
# Revisar dependencias desactualizadas
dotnet list package --outdated
# Frontend
cd web && npm auditConfigurar Dependabot en el repositorio GitHub para PRs automáticos.
Antes de ir a producción:
-
Jwt:Keygenerada con CSPRNG, mínimo 32 chars, no compartida - CORS configurado solo con dominios de producción
- HTTPS forzado (HSTS habilitado)
-
appsettings.Development.jsonexcluido del container de producción - Secrets gestionados vía env vars o vault, no en archivos
- Revisión de logs para no exponer datos sensibles
- Backup automatizado de PostgreSQL configurado
-
dotnet ef migrationsejecutado — no usarEnsureCreated()en producción - Rate limiting revisado y ajustado al volumen esperado
- Logs de auditoría monitoreados con alertas para acciones inusuales
| OWASP Top 10 | Control implementado |
|---|---|
| A01 Broken Access Control | Roles via Identity + JWT claims |
| A02 Cryptographic Failures | TLS obligatorio, JWT con clave fuerte |
| A03 Injection | EF Core parametrizado, FluentValidation |
| A04 Insecure Design | Clean Architecture, validación en capas |
| A05 Security Misconfiguration | Security headers middleware, CORS restringido |
| A06 Vulnerable Components | Dependabot, npm audit, dotnet list --outdated |
| A07 Auth Failures | Rate limiting, JWT con expiración corta |
| A08 Software Integrity | Verificar hashes de imágenes Docker en CI |
| A09 Logging Failures | Serilog + AuditLog entity |
| A10 SSRF | Pendiente — validar URLs en futuras features |
Al ser open source, reportar vulnerabilidades de seguridad abriendo un GitHub Security Advisory (privado) en el repositorio, no un issue público. Incluir pasos reproducibles y versión afectada.