Configuración de sistema (System Configuration)
Configuración de sistema (SC)
Esta sección del checklist original —System Configuration— cubre 13 puntos sobre cómo debe comportarse una aplicación y su entorno cuando nadie tocó la configuración por defecto.
1. Mínimo privilegio en aplicaciones, procesos y cuentas de servicio
Cada componente debe correr con los permisos mínimos necesarios para cumplir su función, nunca con privilegios de administrador “por comodidad”.
# Mal: el proceso corre como root dentro del contenedor
FROM node:20-alpine
COPY . .
CMD ["node", "server.js"]
# Bien: usuario sin privilegios dedicado
FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
COPY --chown=app:app . .
USER app
CMD ["node", "server.js"]
En systemd, lo mismo se logra con directivas como User=, NoNewPrivileges=yes y ProtectSystem=strict en la unit file del servicio.
2. El código de infraestructura también sigue mínimo privilegio
No alcanza con que la aplicación corra sin privilegios: el código que define la infraestructura (Terraform, CloudFormation, Pulumi) debe declarar permisos acotados, no roles genéricos con *.
# Mal: permiso amplio "por si acaso"
resource "aws_iam_role_policy" "demasiado_amplio" {
policy = jsonencode({
Statement = [{ Effect = "Allow", Action = "s3:*", Resource = "*" }]
})
}
# Bien: acción y recurso acotados a lo que la función realmente necesita
resource "aws_iam_role_policy" "acotado" {
policy = jsonencode({
Statement = [{
Effect = "Allow"
Action = ["s3:GetObject", "s3:PutObject"]
Resource = "arn:aws:s3:::mi-bucket-uploads/*"
}]
})
}
3. Eliminar funcionalidad innecesaria
Archivos de instalación, cuentas de demostración, plugins sin usar o paneles de ejemplo que vienen con un framework son superficie de ataque gratuita. Si no se usa, se desinstala o se borra — no se deja “por si acaso” apagado.
4. Remover código de prueba antes de producción
Endpoints de debug, rutas de “testing” (/api/debug/reset-db), feature flags de desarrollo o seeds de datos falsos no deben llegar a la imagen de producción. Esto se controla mejor con builds separados por entorno que con un if (process.env.NODE_ENV !== 'production') disperso por el código.
5. Configuración de seguridad auditable
El almacén de configuración de seguridad de la aplicación (políticas de contraseñas, timeouts de sesión, listas de CORS permitidas, etc.) debe estar en un formato legible por humanos —YAML, JSON, HCL— y versionado en control de código, no embebido en binarios o en una base de datos sin historial de cambios. Esto permite auditar quién cambió qué y cuándo con git blame.
6. Aislar entornos de desarrollo y producción
Desarrollo y producción no deben compartir credenciales, bases de datos ni redes. El acceso a cada entorno se restringe solo a los grupos autorizados (por ejemplo, vía grupos de IAM o roles de Kubernetes RBAC separados por namespace).
7. Sistema de control de cambios
Todo cambio de código o de configuración —en desarrollo y en producción— debe quedar registrado: Pull Requests con revisión obligatoria, pipelines de CI/CD que dejan traza de qué se desplegó y cuándo, y change management para cambios de infraestructura crítica.
8. Evitar que páginas sensibles aparezcan en buscadores
# robots.txt
User-agent: *
Disallow: /admin/
Disallow: /internal-api/
Disallow: /_debug/
X-Robots-Tag: noindex, nofollow
<meta name="robots" content="noindex, nofollow">
El robots.txt es una convención, no un control de acceso: no reemplaza la autenticación, solo evita la indexación accidental. Rutas verdaderamente sensibles deben estar protegidas con autenticación real, no solo ocultas del buscador.
9. Deshabilitar métodos HTTP innecesarios
Métodos como PUT, DELETE o extensiones WebDAV (PROPFIND, MKCOL) no deben estar habilitados si la aplicación no los necesita explícitamente.
# nginx: solo permitir GET, HEAD y POST
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 405;
}
# Apache: bloquear todo excepto GET y POST
<Limit GET POST>
Require all granted
</Limit>
<LimitExcept GET POST>
Require all denied
</LimitExcept>
10. Remover información innecesaria de headers HTTP
Los headers no deberían delatar el sistema operativo, el servidor web o el framework usado —información que facilita elegir el exploit correcto.
// Express
const app = express();
app.disable('x-powered-by');
# nginx.conf
server_tokens off;
; php.ini
expose_php = Off
11. Evitar exponer .git, .svn u otra metadata de control de versiones
location ~ /\.(git|svn|hg) {
deny all;
return 404;
}
Mejor todavía: el pipeline de build debe generar un artefacto que nunca contenga el directorio .git, en vez de confiar solo en el bloqueo del servidor web.
12. No almacenar secretos en texto plano
Contraseñas, connection strings, claves de API y material criptográfico no van hardcodeados en el código fuente, en artefactos de build ni en variables de entorno sin cifrar en reposo.
# Mal
DATABASE_URL=postgres://admin:SuperSecreto123@db:5432/app
# Mejor: referencia a un gestor de secretos, resuelta en runtime
DATABASE_URL_SECRET_REF=arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/db-url
Herramientas típicas: AWS Secrets Manager, HashiCorp Vault, Doppler, o como mínimo .env fuera del control de versiones con .gitignore.
13. Restringir documentación interna y APIs internas
Documentación de APIs internas (Swagger/OpenAPI UI, GraphQL Playground/introspection) no debería estar accesible públicamente en producción, porque revela la superficie completa del backend a un atacante.
// Solo exponer Swagger UI fuera de producción
if (process.env.NODE_ENV !== 'production') {
app.use('/docs', swaggerUi.serve, swaggerUi.setup(spec));
}
# Deshabilitar introspection en producción (Apollo Server)
new ApolloServer({ introspection: process.env.NODE_ENV !== 'production' })