StackPractices
beginner Por Mathias Paulenko

Plantilla de Auditoría de Acceso de Usuarios

Una plantilla para revisar y certificar los derechos de acceso de los usuarios en sistemas, aplicaciones y repositorios de datos.

Visión General

Una auditoría de acceso de usuarios comprueba que cada cuenta de tus sistemas pertenece a alguien que todavía la necesita, con un rol que encaja con lo que realmente hace. Es un control fundamental para la gobernanza de identidades y el mínimo privilegio — y un requisito duro en auditorías SOC 2, ISO 27001 y PCI-DSS. Ejecutadas sin estructura, estas revisiones acaban en una hoja de cálculo que nadie firma; esta plantilla te da las tablas de alcance, el registro de certificación y el plan de remediación para hacer una que los auditores puedan trazar.

Cuándo Usar

  • Al realizar revisiones de acceso trimestrales o anuales.
  • Al preparar una auditoría de cumplimiento o certificación.
  • Tras un cambio de rol, reorganización o fusión.
  • Cuando se sospecha que el acceso privilegiado es excesivo.
  • Tras la baja de un usuario o la salida de un contratista.

Si tu objetivo es revisar el modelo de permisos de un solo sistema en vez de certificar el acceso usuario a usuario, la Plantilla de Revisión de Control de Acceso encaja mejor.

Prerrequisitos

  • Una fuente de identidades como un proveedor SSO o un sistema de gestión de identidades.
  • Una lista de las aplicaciones, sistemas y repositorios de datos bajo revisión.
  • Dueños o responsables de cada aplicación que puedan certificar el acceso.
  • Un calendario de revisiones definido y un proceso de escalado.

Solución

Plantilla

1. Alcance de la Auditoría

Elemento de alcanceDescripción
Periodo2026-Q2
Sistemas revisadosAWS, GitHub, Jira, Confluence, Slack, VPN, Google Workspace
PoblaciónEmpleados, contratistas, cuentas de servicio, roles de administrador privilegiados
RevisoresDueños de aplicaciones, responsables, equipo de seguridad
Fecha límite2026-07-15
Excepciones permitidasSí, con aceptación de riesgo y fecha de vencimiento

2. Inventario de Identidades

ID de usuarioNombreTipoDepartamentoEstadoÚltima revisión
alice@example.comAlice ChenEmpleadaIngenieríaActiva2026-03-31
bob@example.comBob SmithContratistaFinanzasActiva2026-03-31
svc-api-prodServicio APICuenta de servicioPlataformaActiva2026-05-15
carol@example.comCarol JonesEmpleadaMarketingInactiva2026-01-31

3. Mapeo de Accesos

UsuarioSistemaRol / PermisoJustificación de negocioRevisorDecisión
alice@example.comAWSPowerUserGestiona la infraestructuraLíder de plataformaMantener
bob@example.comGitHubLecturaRevisa pull requestsResponsable de ingenieríaMantener
alice@example.comJiraAdminConfigura workflowsLíder de ITRevocar
svc-api-prodAWSS3 solo lecturaLa aplicación lee informesLíder de plataformaMantener
carol@example.comSlackMiembroDejó la empresaRRHHRevocar

4. Revisión de Acceso Privilegiado

UsuarioSistemaRol privilegiadoJustificaciónRiesgoRevisorDecisión
alice@example.comAWSAcceso rootBreak-glass de emergenciaAltoCISOMantener con MFA
dave@example.comGitHubDueño de la organizaciónGestiona repositoriosAltoCTOMantener
eve@example.comVPNTúnel completoAcceso remoto de administraciónAltoLíder de seguridadRevocar

5. Registro de Certificación

AplicaciónRevisorEstadoFechaNotas
AWSLíder de plataformaCertificado2026-07-102 revocaciones pendientes
GitHubCTOCertificado2026-07-081 cuenta huérfana eliminada
JiraLíder de ITEn progreso2026-07-05Rol admin en revisión
SlackRRHHCertificado2026-07-093 cuentas inactivas revocadas

6. Plan de Remediación

HallazgoAcciónResponsableFecha límiteEstado
Derechos de admin excesivos en JiraDegradar a usuarioLíder de IT2026-07-20Abierto
Cuenta inactiva en SlackDesactivarRRHH2026-07-12Hecho
Cuenta de servicio huérfanaInvestigar y deshabilitarEquipo de plataforma2026-07-18Abierto
MFA ausente en usuarios privilegiadosAplicar MFAEquipo de IAM2026-07-15En progreso

Explicación

Cada tabla de la plantilla responde a una pregunta distinta de la auditoría: quién existe (inventario), a qué puede llegar (mapeo), quién puede romper cosas (revisión de privilegiados), quién firmó (certificación) y qué se está arreglando (remediación). Sin esa cadena, las organizaciones acumulan cuentas obsoletas y usuarios con privilegios de más — los dos hallazgos que los auditores externos levantan con más frecuencia.

La frecuencia de cada capa depende del riesgo de lo que protege:

Tipo de accesoCadencia recomendadaPor qué
Privilegiado (admin, root, break-glass)TrimestralMayor radio de impacto
Acceso de empleados/contratistasAnual, más al cambiar de rolLos roles derivan más rápido que la plantilla
Cuentas de servicioSemestral, más al cambiar desplieguesNadie se da cuenta cuando quedan obsoletas
Contratistas/externosCada 90 días o al terminar el contratoEl acceso debería morir con el contrato

Los disparadores “más al ocurrir X” importan más que el calendario. Un calendario trimestral sigue dejando escapar privilegios cuando alguien cambia de equipo en la semana dos y conserva sus permisos de admin diez semanas: conecta la revocación a tus flujos de baja y transferencia, no solo al ciclo de revisión.

Flujo de auditoría de acceso: exportar inventario, mapear accesos, revisores certifican, remediar hallazgos, archivar evidencia

Los auditores no evalúan las tablas — evalúan el rastro. Para cada decisión de “mantener” quieren un nombre, una fecha y una justificación; para cada “revocar” quieren pruebas de que la cuenta murió de verdad. Esa asimetría es la razón por la que el registro de certificación y el paquete de evidencia importan tanto como la revisión en sí: una serie de decisiones impecables sin rastro documental se lee como “no ejecutada” en una prueba de campo de SOC 2.

El tamaño también cambia la mecánica. Con quince personas, la plantilla de arriba es todo el proceso: una persona exporta las listas, los responsables certifican en un documento compartido, listo en una semana. A partir de cien usuarios, la tabla de mapeo por usuario deja de caber en una pantalla y querrás los scripts de automatización más un responsable por aplicación en vez de un solo revisor para todo. Las tablas son las mismas en ambos casos; lo que escala es quién las rellena y cómo llegan los datos.

Cómo Ejecutar la Auditoría

Recorre la plantilla en este orden — cada paso alimenta al siguiente:

  1. Rellena primero la tabla de alcance. Lista todos los sistemas en revisión y nombra a su revisor antes de tocar datos. Las revisiones que fracasan suelen hacerlo aquí: un sistema que nadie posee se omite en silencio.
  2. Exporta el inventario de identidades desde tu SSO o IdP (Okta, Entra ID, Google Workspace). Extrae todos los tipos de cuenta — empleados, contratistas, cuentas de servicio — porque las cuentas de servicio son las que nadie revisa. Automatízalo con el script de Okta de Soluciones Avanzadas si puedes.
  3. Mapea el acceso por usuario y sistema. Para cada aplicación, exporta la lista de usuarios con sus roles y concíliala contra el inventario. Cualquier cuenta que exista en la app pero no en el IdP es una huérfana candidata: márcala de inmediato en vez de esperar a la fase de remediación.
  4. Envía la tabla de privilegiados a un revisor senior. El acceso privilegiado necesita un segundo par de ojos con autoridad — CISO, líder de seguridad o CTO. Los responsables tienden a aprobar los permisos de admin de su propio equipo por defecto; un revisor aparte cambia eso.
  5. Recoge las certificaciones. Cada dueño de aplicación firma su sistema en el registro. “Certificado” significa que miró, no que pulsó aprobar: exige una decisión (mantener, revocar, degradar) en cada fila.
  6. Abre un ítem de remediación por cada revocación o degradación, con responsable y fecha límite. La auditoría no termina cuando acaba la revisión: termina cuando se cierra el último ítem de remediación.
  7. Archiva la evidencia. Guarda juntas las listas exportadas, las certificaciones firmadas y los tickets de remediación cerrados. Tu auditor de SOC 2 o ISO te pedirá exactamente ese paquete.

Reserva tres semanas para un ciclo completo la primera vez: una para recolección de datos y mapeo, una para certificaciones y una para remediación. Se comprime a unas dos semanas cuando las exportaciones ya están scripteadas y los revisores conocen la dinámica. Ejecuta chequeos puntuales de solo acceso privilegiado entre ciclos completos para que la revisión profunda trimestral no sea tu única red de seguridad.

Ejemplo resuelto: auditoría del Q2 en una SaaS de 50 personas

Población: 47 empleados, 6 contratistas, 12 cuentas de servicio
Sistemas: AWS, GitHub, Jira, Slack, VPN, Stripe

Hallazgos:
  - 4 cuentas inactivas (1 empleado que se fue, 3 contratistas)
  - 2 usuarios con admin en Jira y GitHub (esperado: solo líderes de ingeniería)
  - 1 cuenta de servicio huérfana (svc-reporting, sin deploy desde 2025-11)
  - 7 usuarios sin MFA en la VPN
  - 1 contratista con acceso 60 días tras el fin de contrato

Remediación:
  - 5 cuentas desactivadas en 48 horas
  - Admin de Jira degradado para 1 usuario; el segundo confirmado como legítimo
  - svc-reporting deshabilitada tras confirmar con plataforma que no tenía dependencias
  - MFA forzado con política de acceso condicional
  - Registro de contratista corregido; RRHH añadió caducidad automática a cuentas externas

Tiempos del ciclo: reunión de alcance el lunes, certificaciones el viernes
de la semana 2, remediación cerrada el día 18 de la ventana de 21 días.

Fíjate en el patrón de los hallazgos: cuatro de cinco fueron fallos de proceso, no de actores maliciosos — un empleado que se fue cuyo acceso quedó colgado, una cuenta de servicio sin dueño, un contratista cuya fecha de fin nadie registró. Es lo típico. Las auditorías de acceso rara vez cazan abusos; cazan entropía.

Variantes

El mismo esqueleto escala a alcances más estrechos — cambia la población, conserva las tablas:

  • Revisión de acceso de una aplicación: se centra en un solo sistema, como AWS IAM o la organización de GitHub.
  • Revisión de acceso privilegiado: solo revisa cuentas de admin, root o de emergencia.
  • Auditoría de cuentas de servicio: revisa identidades no humanas y sus claves de API o credenciales. Combínala con la Plantilla de Programa de Rotación de Secretos: las credenciales de las cuentas de servicio que mantengas deberían tener una cadencia de rotación.
  • Revisión de acceso de contratistas: revisión con plazo para usuarios externos con acceso temporal.
  • Auditoría de acceso a datos: se centra en usuarios que pueden acceder a bases de datos sensibles, data lakes o herramientas de analítica.

Lo Que Funciona

  • Extrae el inventario de identidades del SSO automáticamente — las exportaciones manuales quedan desactualizadas antes de que termine la revisión.
  • Envía a los revisores un recordatorio una semana antes de la fecha límite y luego diario los últimos tres días. Las certificaciones estancadas son la primera causa de auditorías vencidas.
  • Exige una justificación de negocio escrita para cada rol privilegiado: “gestiona la infraestructura” pasa, “lo necesita” no.
  • Revoca al cambiar de rol y al salir de inmediato; la revisión trimestral es la red de seguridad, no el disparador.
  • Documenta cada excepción con aceptación de riesgo y fecha de caducidad — las excepciones permanentes no son excepciones.
  • Sigue la remediación hasta el cierre. Una auditoría que encuentra problemas y no arregla ninguno es peor que no auditar, porque ahora lo sabías.

Errores Comunes

  • Revisar el acceso una vez al año sin seguimiento: los hallazgos quedan abiertos hasta el siguiente ciclo.
  • Dejar que los responsables conserven permisos de admin para gente que se transfirió a su equipo.
  • Ignorar las cuentas de servicio y las credenciales compartidas: no salen en el organigrama pero tienen privilegios reales.
  • Saltarse las cuentas break-glass y de emergencia porque “casi no las usamos” — justo por eso necesitan revisión.
  • Aceptar decisiones de “mantener” sin justificación de negocio en las filas privilegiadas.
  • Marcar ítems como “hechos” en el plan de remediación sin verificar que la revocación ocurrió de verdad en el sistema.
  • Guardar la evidencia repartida en correos y chats dispersos: los auditores necesitan un solo paquete por ciclo.

Troubleshooting

  • Los revisores ignoran las peticiones de certificación: escala a sus responsables tras el segundo recordatorio, y convierte la certificación en un entregable con seguimiento, no en un favor.
  • Las exportaciones del inventario no reflejan la realidad: el IdP solo ve las cuentas gestionadas. Cruza directamente la lista de usuarios locales de cada aplicación — las cuentas ocultas viven ahí.
  • Demasiados hallazgos para remediar en un ciclo: prioriza el acceso privilegiado, luego las cuentas obsoletas y después los huecos de justificación. No intentes cerrarlo todo; intenta cerrar lo peligroso.
  • Cuentas de servicio que nadie posee: asigna un dueño antes de rotar o deshabilitar nada — matar una cuenta sin dueño es cómo se rompe producción un viernes.
  • No sabes quién usa una cuenta compartida: activa el registro por sesión durante dos semanas y mapea las sesiones a personas. La Plantilla de Política RBAC te ayuda a diseñar los roles individuales a los que los migrarás.

Soluciones Avanzadas

Revisión de acceso automatizada con la API de Okta

Extrae los datos de acceso de usuarios desde Okta y genera un informe de revisión automáticamente:

import requests
import csv
from datetime import datetime, timedelta, timezone
from dataclasses import dataclass
from typing import List

@dataclass
class UserAccessRecord:
    user_id: str
    user_name: str
    status: str
    last_login: str
    assigned_apps: List[str]
    admin_roles: List[str]

class OktaAccessReviewer:
    def __init__(self, api_token: str, domain: str):
        self.headers = {
            "Authorization": f"SSWS {api_token}",
            "Accept": "application/json",
        }
        self.base_url = f"https://{domain}/api/v1"

    def get_inactive_users(self, days: int = 90) -> List[dict]:
        """Encuentra usuarios que no han iniciado sesión en el periodo indicado."""
        cutoff = datetime.now(timezone.utc) - timedelta(days=days)
        users = []
        params = {"filter": 'status eq "ACTIVE"'}
        resp = requests.get(
            f"{self.base_url}/users",
            headers=self.headers,
            params=params,
        )
        for user in resp.json():
            last_login = user.get("lastLogin")
            if not last_login:
                continue
            login_time = datetime.fromisoformat(last_login.replace("Z", "+00:00"))
            if login_time < cutoff:
                users.append({
                    "id": user["id"],
                    "email": user["profile"]["email"],
                    "last_login": last_login,
                    "status": user["status"],
                })
        return users

    def get_user_apps(self, user_id: str) -> List[str]:
        """Obtiene las aplicaciones asignadas a un usuario."""
        resp = requests.get(
            f"{self.base_url}/users/{user_id}/appLinks",
            headers=self.headers,
        )
        return [app["label"] for app in resp.json()]

    def get_admin_roles(self, user_id: str) -> List[str]:
        """Obtiene los roles de administrador asignados a un usuario."""
        resp = requests.get(
            f"{self.base_url}/users/{user_id}/roles",
            headers=self.headers,
        )
        return [role["type"] for role in resp.json()]

    def generate_review_report(self, output_file: str) -> None:
        """Genera un informe CSV de todos los usuarios activos y su acceso."""
        resp = requests.get(
            f"{self.base_url}/users",
            headers=self.headers,
            params={"filter": 'status eq "ACTIVE"'},
        )
        with open(output_file, "w", newline="") as f:
            writer = csv.writer(f)
            writer.writerow([
                "User ID", "Email", "Status", "Last Login",
                "Assigned Apps", "Admin Roles"
            ])
            for user in resp.json():
                apps = self.get_user_apps(user["id"])
                roles = self.get_admin_roles(user["id"])
                writer.writerow([
                    user["id"],
                    user["profile"]["email"],
                    user["status"],
                    user.get("lastLogin", "Never"),
                    "; ".join(apps),
                    "; ".join(roles),
                ])

# Ejemplo de uso
reviewer = OktaAccessReviewer(api_token="YOUR_TOKEN", domain="yourorg.okta.com")
inactive = reviewer.get_inactive_users(days=90)
for u in inactive:
    print(f"INACTIVE: {u['email']} - last login: {u['last_login']}")
reviewer.generate_review_report("access_review_q2.csv")

AWS IAM Access Analyzer para la detección automatizada de hallazgos

Usa AWS IAM Access Analyzer para detectar permisos sin usar y accesos entre cuentas:

#!/bin/bash
set -euo pipefail

# Crea un analyzer si todavía no existe
ANALYZER_NAME="org-access-analyzer"
ANALYZER_ARN=$(aws accessanalyzer list-analyzers \
  --type ORGANIZATION \
  --query 'analyzers[?analyzerName==`'"$ANALYZER_NAME"'`].arn | [0]' \
  --output text)

if [ -z "$ANALYZER_ARN" ] || [ "$ANALYZER_ARN" = "None" ]; then
  ANALYZER_ARN=$(aws accessanalyzer create-analyzer \
    --analyzer-name "$ANALYZER_NAME" \
    --type ORGANIZATION \
    --region us-east-1 \
    --query 'analyzer.arn' \
    --output text)
fi

# Lista todos los hallazgos
echo "=== Active Findings ==="
aws accessanalyzer list-findings \
  --analyzer-arn "$ANALYZER_ARN" \
  --filter '{"status":{"eq":["ACTIVE"]}}' \
  --query 'findings[*].{id:id,resource:resource.resourceArn,type:findingType,createdAt:createdAt}' \
  --output table

# Exporta los hallazgos a JSON para el registro de auditoría
aws accessanalyzer list-findings \
  --analyzer-arn "$ANALYZER_ARN" \
  --query 'findings[*].{id:id,resource:resource.resourceArn,type:findingType,createdAt:createdAt}' \
  --output json > iam-findings-$(date +%Y%m%d).json

Script de auditoría de la organización de GitHub

Audita los miembros de la organización de GitHub y sus roles de forma programática:

const { Octokit } = require("@octokit/rest");

async function auditGitHubOrg(orgName, token) {
  const octokit = new Octokit({ auth: token });
  const findings = [];

  // Obtiene todos los miembros de la organización
  const members = await octokit.paginate(
    octokit.rest.orgs.listMembers,
    { org: orgName, per_page: 100 }
  );

  // Miembros con 2FA deshabilitado — hay un filtro dedicado en la API;
  // el flag no viene en el payload de miembros por defecto
  const no2faMembers = await octokit.paginate(
    octokit.rest.orgs.listMembers,
    { org: orgName, filter: "2fa_disabled", per_page: 100 }
  );
  const no2faLogins = new Set(no2faMembers.map((m) => m.login));

  for (const member of members) {
    // Obtiene el rol del usuario vía el endpoint de membresía
    const { data: membership } = await octokit.rest.orgs.getMembershipForUser({
      org: orgName,
      username: member.login,
    });

    findings.push({
      login: member.login,
      role: membership.role,
      two_factor: no2faLogins.has(member.login) ? "disabled" : "enabled",
    });
  }

  // Marca los usuarios sin 2FA
  const noMfa = findings.filter((f) => f.two_factor === "disabled");
  if (noMfa.length > 0) {
    console.log(`\nUSERS WITHOUT 2FA (${noMfa.length}):`);
    noMfa.forEach((u) => console.log(`  - ${u.login} (role: ${u.role})`));
  }

  // Marca los administradores
  const admins = findings.filter((f) => f.role === "admin");
  console.log(`\nORGANIZATION ADMINS (${admins.length}):`);
  admins.forEach((u) => console.log(`  - ${u.login} (2FA: ${u.two_factor})`));

  return findings;
}

auditGitHubOrg("your-org", process.env.GITHUB_TOKEN)
  .then(() => console.log("\nAudit complete."))
  .catch((err) => console.error("Audit failed:", err.message));

Checklist de Remediación de Cuentas Compartidas

  • Inventariar todas las cuentas compartidas (root, admin, servicio)
  • Identificar qué personas usan cada cuenta compartida
  • Crear cuentas individuales para cada usuario
  • Deshabilitar la cuenta compartida tras la migración
  • Implementar un procedimiento break-glass para el acceso de emergencia
  • Registrar todo el uso de break-glass con alertas automáticas

Lecturas Adicionales

Preguntas frecuentes

¿Quién debería certificar el acceso?

El dueño del sistema o el responsable directo del usuario es el mejor revisor — quien pueda responder de que la persona sigue haciendo el trabajo que necesita ese acceso. Para sistemas privilegiados y sensibles, envíalo al equipo de seguridad o al dueño de los datos como segundo aprobador.

¿Qué es una cuenta huérfana?

Una cuenta huérfana es una cuenta activa que ya no corresponde a ninguna persona o dueño conocido — suele quedar tras una baja, un cambio de equipo o una migración. Deberían deshabilitarse a la espera de investigación y después reclamarse o eliminarse.

¿Con qué frecuencia hay que revisar el acceso privilegiado frente al general?

Privilegiado trimestralmente, general anualmente — con revisiones por disparador entre ciclos. Cualquier cambio de rol, transferencia, baja o incidente de seguridad debería lanzar una revisión puntual sin importar en qué punto del ciclo caigas.

¿Cómo hacemos las revisiones de acceso menos tediosas?

Automatiza la recolección de datos y deja las decisiones a las personas. Las herramientas de gobernanza de identidades (o los scripts de abajo) extraen los inventarios y marcan las cuentas inactivas automáticamente, así los revisores dedican su tiempo a decidir, no a copiar y pegar CSVs.

¿Qué hacemos con las cuentas que necesitan acceso legítimo pero temporal?

Concédelas con fecha de caducidad, no abiertas. Regístralas como excepciones con aceptación de riesgo y una fecha, y deja que el siguiente ciclo de revisión fuerce la retirada — o mejor, usa los controles de acceso temporal de tu IdP para que la caducidad sea automática.