beginner Por Mathias Paulenko

Plantilla de Auditoria de Acceso de Usuarios

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

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Descripcion General

Una auditoria de acceso de usuarios verifica que cada usuario tenga el nivel correcto de acceso a sistemas, aplicaciones y datos. Es un control fundamental para la gobernanza de identidades, el principio de minimo privilegio y el cumplimiento de estandares como SOC 2, ISO 27001 y PCI-DSS. Esta plantilla proporciona una forma estructurada de recopilar datos de acceso, revisar permisos, certificar accesos y remediar hallazgos.

Cuando Usar

  • For alternatives, see Access Control Review Template.

  • Realizar revisiones de acceso trimestrales o anuales.

  • Prepararse para una auditoria de cumplimiento o certificacion.

  • Despues de un cambio de rol, reorganizacion o fusion.

  • Cuando se sospecha de acceso privilegiado excesivo.

  • Despues de la baja de un usuario o contratista.

Prerequisitos

  • Una fuente de identidades como un proveedor SSO o sistema de gestion de identidades.
  • Una lista de aplicaciones, sistemas y repositorios de datos bajo revision.
  • Duenos o gerentes de cada aplicacion que puedan certificar el acceso.
  • Un cronograma de revisiones de acceso y un proceso de escalamiento.

Solucion

Plantilla

1. Alcance de la Auditoria

Item de AlcanceDescripcion
Periodo2026-Q2
Sistemas revisadosAWS, GitHub, Jira, Confluence, Slack, VPN, Google Workspace
PoblacionEmpleados, contratistas, cuentas de servicio, roles admin privilegiados
RevisoresDuenos de aplicaciones, gerentes, equipo de seguridad
Fecha limite2026-07-15
Excepciones permitidasSi, con aceptacion de riesgo y vencimiento

2. Inventario de Identidades

ID de UsuarioNombreTipoDepartamentoEstadoUltima Revision
alice@example.comAlicia ChenEmpleadaIngenieriaActiva2026-03-31
bob@example.comRoberto SmithContratistaFinanzasActiva2026-03-31
svc-api-prodServicio APICuenta de servicioPlataformaActiva2026-05-15
carol@example.comCarolina JonesEmpleadaMarketingInactiva2026-01-31

3. Mapeo de Acceso

UsuarioSistemaRol / PermisoJustificacion de NegocioRevisorDecision
alice@example.comAWSPowerUserGestiona infraestructuraLider de plataformaMantener
bob@example.comGitHubLecturaRevisa pull requestsGerente de ingenieriaMantener
alice@example.comJiraAdminConfigura workflowsLider de ITRevocar
svc-api-prodAWSS3 solo lecturaAplicacion lee reportesLider de plataformaMantener
carol@example.comSlackMiembroDejo la empresaRRHHRevocar

4. Revision de Acceso Privilegiado

UsuarioSistemaRol PrivilegiadoJustificacionRiesgoRevisorDecision
alice@example.comAWSAcceso rootBreak-glass de emergenciaAltoCISOMantener con MFA
dave@example.comGitHubDueno de organizacionGestiona repositoriosAltoCTOMantener
eve@example.comVPNTunel completoAcceso remoto adminAltoLider de seguridadRevocar

5. Registro de Certificacion

AplicacionRevisorEstadoFechaNotas
AWSLider de plataformaCertificado2026-07-102 revocaciones pendientes
GitHubCTOCertificado2026-07-081 cuenta huerfana eliminada
JiraLider de ITEn progreso2026-07-05Rol admin en revision
SlackRRHHCertificado2026-07-093 cuentas inactivas revocadas

6. Plan de Remediacion

HallazgoAccionDuenoFecha LimiteEstado
Derechos admin excesivos en JiraDegradar a usuarioLider de IT2026-07-20Abierto
Cuenta inactiva en SlackDesactivarRRHH2026-07-12Hecho
Cuenta de servicio huerfanaInvestigar y deshabilitarEquipo de plataforma2026-07-18Abierto
MFA faltante en usuarios privilegiadosAplicar MFAEquipo IAM2026-07-15En progreso

Explicacion

La plantilla conecta identidades con permisos, justificacion de negocio y revisores responsables. Sin esta estructura, las organizaciones acumulan cuentas obsoletas y usuarios sobre-privilegiados, aumentando tanto el riesgo interno como la superficie de ataque externa. Las revisiones de acceso regulares son requeridas por la mayoria de los frameworks de seguridad y son una forma practica de aplicar el minimo privilegio.

Variantes

  • Revision de acceso especifica a una aplicacion: Se enfoca en un solo sistema, como AWS IAM o acceso a organizacion de GitHub.
  • Revision de acceso privilegiado: Solo revisa cuentas de admin, root o acceso de emergencia.
  • Auditoria de cuentas de servicio: Revisa identidades no humanas y sus claves API o credenciales.
  • Revision de acceso de contratistas: Revision con plazo definido para usuarios externos con acceso temporal.
  • Auditoria de acceso a datos: Se enfoca en usuarios que pueden acceder a bases de datos sensibles, data lakes o herramientas de analisis.

Lo que funciona

  • Automatiza la recoleccion de identidades desde el proveedor SSO o gestion de identidades.
  • Envia recordatorios a los revisores antes de la fecha limite.
  • Requiere justificacion de negocio para cada rol privilegiado.
  • Revoca el acceso inmediatamente cuando un usuario cambia de rol o se va.
  • Programa revisiones trimestrales para acceso privilegiado y anuales para acceso general.
  • Documenta la aceptacion de riesgo para excepciones necesarias.
  • Rastrea la remediacion hasta que cada hallazgo se cierre.

Errores Comunes

  • Revisar el acceso solo una vez al año sin seguimiento.
  • Permitir que gerentes mantengan acceso para empleados que cambiaron de rol.
  • Ignorar cuentas de servicio y credenciales compartidas.
  • Omitir acceso privilegiado o cuentas de break-glass de emergencia.
  • No vincular decisiones de acceso a justificacion de negocio.
  • No verificar que las revocaciones realmente ocurrieron.
  • Almacenar evidencia de revision en correos o documentos dispersos.

Soluciones Avanzadas

Revision automatizada de acceso con Okta API

Extrae datos de acceso de usuarios desde Okta y genera un reporte de revision automaticamente:

import requests
import csv
from datetime import datetime, timedelta
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]:
        """Find users who haven't logged in within the specified period."""
        cutoff = (datetime.utcnow() - timedelta(days=days)).isoformat() + "Z"
        users = []
        params = {"filter": f'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 last_login and last_login < 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]:
        """Get applications assigned to a user."""
        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]:
        """Get admin roles assigned to a user."""
        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:
        """Generate a CSV report of all active users and their access."""
        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),
                ])

# Example usage
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 deteccion automatizada de hallazgos

Usa AWS IAM Access Analyzer para detectar permisos no utilizados y acceso entre cuentas:

#!/bin/bash
set -euo pipefail

# Create an analyzer if it doesn't exist
ANALYZER_NAME="org-access-analyzer"
aws accessanalyzer create-analyzer \
  --analyzer-name "$ANALYZER_NAME" \
  --type ORGANIZATION \
  --region us-east-1

# List all findings
echo "=== Active Findings ==="
aws accessanalyzer list-findings \
  --analyzer-arn "$(aws accessanalyzer list-analyzers --query 'analyzers[0].arn' --output text)" \
  --filter '{"status":{"eq":["ACTIVE"]}}' \
  --query 'findings[*].{id:id,resource:resource.resourceArn,type:findingType,createdAt:createdAt}' \
  --output table

# Export findings to CSV for audit trail
aws accessanalyzer list-findings \
  --analyzer-arn "$(aws accessanalyzer list-analyzers --query 'analyzers[0].arn' --output text)" \
  --query 'findings[*].{id:id,resource:resource.resourceArn,type:findingType,createdAt:createdAt}' \
  --output json > iam-findings-$(date +%Y%m%d).json

Script de auditoria de organizacion de GitHub

Audita miembros de la organizacion de GitHub y sus roles programaticamente:

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

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

  // Get all organization members
  const members = await octokit.paginate(
    octokit.rest.orgs.listMembers,
    { org: orgName, per_page: 100 }
  );

  for (const member of members) {
    // Check if 2FA is enabled
    const { data: mfaStatus } = await octokit.rest.orgs.getMembershipForUser({
      org: orgName,
      username: member.login,
    });

    // Get user's public keys to verify SSH key rotation
    let keyCount = 0;
    try {
      const { data: keys } = await octokit.rest.users.listPublicKeysForUser({
        username: member.login,
      });
      keyCount = keys.length;
    } catch (e) {
      // API may rate limit
    }

    findings.push({
      login: member.login,
      role: mfaStatus.role,
      two_factor: member.two_factor_authentication ? "enabled" : "disabled",
      public_keys: keyCount,
    });
  }

  // Flag users without 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})`));
  }

  // Flag admins
  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));

Shared Account Remediation Checklist

  • Inventory all shared accounts (root, admin, service)
  • Identify which individuals use each shared account
  • Create individual accounts for each user
  • Disable shared account after migration
  • Implement break-glass procedure for emergency access
  • Log all break-glass usage with automatic alerts

Preguntas frecuentes

Quien debe certificar el acceso?
El dueno del sistema o el gerente directo del usuario es el mejor revisor. Para sistemas sensibles, el equipo de seguridad o el dueno de datos tambien pueden aprobar.
Que es una cuenta huerfana?
Una cuenta huerfana es una cuenta activa que ya no esta asociada a un usuario o dueno conocido, frecuentemente despues de una baja o cambio de equipo. Estas deben deshabilitarse o reclamarse.
Como hacemos las revisiones de acceso menos tediosas?
Utiliza herramientas de gobernanza de identidades que extraigan datos de acceso automaticamente, proporcionen dashboards amigables para revisores y revoquen automaticamente cuentas inactivas de bajo...