Modelos de control de acceso

Objetivos: Al terminar este tema, podrás…

  • Diseñar sistemas de control de acceso apropiados para diferentes contextos
  • Implementar los modelos DAC, MAC y RBAC según requisitos de seguridad
  • Aplicar el principio de privilegio mínimo en arquitecturas de software
  • Identificar y mitigar riesgos de configuración de permisos

Control de acceso como disciplina de ingeniería

El control de acceso responde a tres preguntas fundamentales:

  1. Identificación: ¿Quién eres? (username, certificado)
  2. Autenticación: ¿Cómo lo pruebas? (contraseña, MFA)
  3. Autorización: ¿Qué puedes hacer? (permisos, roles)

Como ingeniero, diseñarás sistemas que implementen estas decisiones de manera:

  • Consistente: Las mismas reglas se aplican en todos los puntos de entrada
  • Auditable: Cada decisión de acceso queda registrada
  • Escalable: El modelo funciona con miles de usuarios y recursos
  • Mantenible: Los cambios de permisos son predecibles

El principio de privilegio mínimo

Definición: Cada entidad (usuario, proceso, sistema) debe tener únicamente los permisos estrictamente necesarios para realizar su función.

Implementación práctica

ContextoAplicación del principio
UsuariosRol base sin permisos, agregar según función
ServiciosService accounts con permisos específicos
APIsScopes limitados por funcionalidad
ContainersNon-root, read-only filesystem donde sea posible
Bases de datosUsuarios separados por aplicación, permisos por tabla

Beneficios de seguridad

  1. Contención de brechas: Cuenta comprometida = daño limitado
  2. Reducción de superficie de ataque: Menos permisos = menos vectores
  3. Facilita auditoría: Permisos específicos = acciones trazables
  4. Previene errores: Menos capacidad de hacer daño accidental

Modelos de control de acceso

1. DAC (Discretionary Access Control)

Principio: El propietario del recurso decide quién accede.

Arquitectura:

Usuario propietario → Define ACL → Recurso
                          ↓
                    Otros usuarios

Implementación típica: Access Control Lists (ACL)

{
  "resource": "/documents/proyecto-final.pdf",
  "owner": "maria@empresa.com",
  "acl": [
    {"principal": "juan@empresa.com", "permissions": ["read"]},
    {"principal": "pedro@empresa.com", "permissions": ["read", "write"]},
    {"principal": "grupo:desarrollo", "permissions": ["read"]}
  ]
}

Casos de uso:

  • Sistemas de archivos (POSIX permissions, NTFS)
  • Almacenamiento en nube (Google Drive, Dropbox, S3)
  • Wikis y documentos colaborativos

Consideraciones de diseño:

VentajaDesventaja
Flexible para usuariosDifícil de auditar a escala
IntuitivoPropenso a over-sharing
DescentralizadoNo hay política central

Vulnerabilidades comunes:

  • Confused deputy: Proceso con más permisos ejecuta acción para usuario con menos permisos
  • Permission creep: Usuarios acumulan permisos con el tiempo
  • Excessive sharing: “Cualquiera con el enlace” en documentos sensibles

2. MAC (Mandatory Access Control)

Principio: El sistema impone reglas de acceso basadas en clasificación. Nadie puede cambiar estas reglas.

Arquitectura:

Política central → Clasifica → Sujetos (usuarios)
        ↓                          ↓
   Define reglas              Etiquetas de seguridad
        ↓                          ↓
   Clasifica → Objetos (recursos) → Comparación → Decisión

Modelo Bell-LaPadula (Confidencialidad)

ReglaDescripciónEjemplo
No Read UpNo leer datos de nivel superiorConfidencial no lee Top Secret
No Write DownNo escribir a nivel inferiorTop Secret no escribe a Público

Modelo Biba (Integridad)

ReglaDescripciónPropósito
No Read DownNo leer de nivel inferiorEvitar contaminación
No Write UpNo escribir a nivel superiorEvitar corrupción

Implementaciones:

  • SELinux (Linux)
  • AppArmor (Linux)
  • Mandatory Integrity Control (Windows)
  • iOS/Android sandboxing

Casos de uso:

  • Sistemas militares y gubernamentales
  • Entornos regulados (HIPAA, PCI-DSS)
  • Contenedores y sandboxes de aplicaciones

Configuración SELinux (ejemplo):

# Contexto de seguridad
ls -Z /var/www/html/
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
 
# Apache solo puede leer archivos con tipo httpd_sys_content_t
# No puede acceder a /etc/shadow aunque root sea propietario

3. RBAC (Role-Based Access Control)

Principio: Los permisos se asignan a roles, no a usuarios. Los usuarios heredan permisos del rol asignado.

Arquitectura:

Usuario → asignado a → Rol → tiene → Permisos → sobre → Recursos

Componentes:

  1. Usuarios: Entidades que solicitan acceso
  2. Roles: Conjuntos de permisos que representan funciones
  3. Permisos: Operaciones permitidas sobre recursos
  4. Sesiones: Activación de roles por usuario

Jerarquía de roles:

                    [Admin]
                       ↓
            [Manager]     [Auditor]
               ↓
    [Editor]    [Viewer]
       ↓
  [Contributor]

Los roles superiores heredan permisos de los inferiores.

Diseño de matriz de permisos:

Rol / RecursoUsuariosProductosPedidosReportesConfig
AdminCRUDCRUDCRUDCRUDCRUD
ManagerRCRUDCRUDCRR
Vendedor-RCR--
ClienteR (propio)RCR (propio)--
AuditorRRRRR

C=Create, R=Read, U=Update, D=Delete

Implementación en código:

class RBACMiddleware:
    def check_permission(self, user, resource, action):
        user_roles = self.get_user_roles(user)
        required_permissions = self.get_required_permissions(resource, action)
 
        for role in user_roles:
            role_permissions = self.get_role_permissions(role)
            if required_permissions.issubset(role_permissions):
                return True
 
        raise AuthorizationError(f"{user} no tiene permiso para {action} en {resource}")

4. ABAC (Attribute-Based Access Control)

Principio: Decisiones basadas en atributos del sujeto, recurso, acción y contexto.

Arquitectura:

Sujeto (atributos) + Recurso (atributos) + Contexto (atributos)
                              ↓
                      Motor de políticas
                              ↓
                      Decisión (permitir/denegar)

Ejemplo de política:

PERMITIR acceso a recurso SI:
  - usuario.departamento == recurso.departamento
  - usuario.nivel_clearance >= recurso.clasificacion
  - contexto.hora ENTRE 08:00 Y 18:00
  - contexto.ubicacion EN ["oficina", "vpn"]

Casos de uso:

  • Políticas dinámicas y contextuales
  • Multi-tenancy complejo
  • Regulaciones que varían por jurisdicción

Comparación de modelos

CaracterísticaDACMACRBACABAC
ControlPropietarioSistemaAdminPolíticas
FlexibilidadAltaBajaMediaMuy alta
EscalabilidadBajaAltaAltaAlta
ComplejidadBajaAltaMediaAlta
AuditoríaDifícilFácilFácilMedia
Uso típicoArchivos personalesMilitar/reguladoEmpresasCloud/SaaS

Diseño de sistemas de control de acceso

Proceso de diseño

  1. Identificar recursos a proteger
  2. Definir operaciones posibles sobre cada recurso
  3. Identificar actores (usuarios, sistemas, servicios)
  4. Mapear funciones a roles (para RBAC)
  5. Definir políticas de acceso
  6. Implementar enforcement en todos los puntos de entrada
  7. Diseñar auditoría de decisiones de acceso

Patrones de implementación

Centralización de decisiones:

[Request] → [API Gateway] → [Authorization Service] → [Policy Engine]
                                      ↓
                              Decisión + Logging
                                      ↓
                              [Microservicio]

Tecnologías:

  • Open Policy Agent (OPA)
  • Casbin
  • AWS IAM
  • Keycloak

Anti-patrones a evitar

Anti-patrónProblemaSolución
Verificar solo en frontendBypass trivialVerificar siempre en backend
Hardcodear permisosInflexible, riesgosoExternalizar a servicio de autorización
Rol “superuser” compartidoSin accountabilityRoles individuales con auditoría
Permisos por exclusiónDifícil de auditarPermisos explícitos (whitelist)

Caso de estudio: Diseño para sistema universitario

Requisitos:

  • 50,000 estudiantes
  • 2,000 profesores
  • 500 administrativos
  • Múltiples sistemas: notas, matrícula, biblioteca, correo

Diseño RBAC:

Roles base:
├── Estudiante
│   ├── Puede: ver propias notas, inscribir materias, acceder biblioteca
│   └── No puede: modificar notas, ver datos de otros estudiantes
├── Profesor
│   ├── Hereda: permisos de Estudiante
│   ├── Puede: modificar notas de sus cursos, ver lista de estudiantes
│   └── No puede: modificar notas de otros cursos
├── Director_Programa
│   ├── Hereda: permisos de Profesor
│   └── Puede: ver estadísticas de programa, aprobar excepciones
├── Administrativo_Registro
│   ├── Puede: gestionar matrículas, generar reportes
│   └── No puede: modificar notas
└── Admin_Sistema
    └── Puede: gestionar usuarios, configurar sistema

Matriz de recursos:

RecursoEstudianteProfesorAdmin_RegistroAdmin_Sistema
Notas propiasRRRR
Notas de curso-RU (sus cursos)RR
Inscripción propiaCRURCRUDCRUD
Inscripción otros--CRUDR
Usuarios--RCRUD

Conceptos clave

TérminoDefinición
Control de accesoMecanismos que determinan quién puede acceder a qué recursos
DACModelo donde el propietario del recurso define el acceso
MACModelo donde el sistema impone reglas de clasificación
RBACModelo donde permisos se asignan a roles, no a usuarios
ABACModelo basado en atributos de sujeto, recurso y contexto
Privilegio mínimoPrincipio de otorgar solo permisos necesarios
ACLLista de permisos asociada a un recurso

Ponte a prueba

  1. Diseño de roles: Una startup de e-commerce tiene: clientes, vendedores, staff de soporte, administradores, y un equipo de finanzas. Diseña un sistema RBAC con roles y permisos para: productos, pedidos, usuarios, pagos, y reportes.

  2. Análisis de riesgos: En el sistema universitario descrito, ¿qué pasaría si un estudiante lograra obtener el rol de profesor? Identifica al menos 3 riesgos y propón controles.

  3. Selección de modelo: Para cada escenario, indica qué modelo (DAC, MAC, RBAC, ABAC) sería más apropiado:

    • Sistema de archivos de una agencia de inteligencia
    • Plataforma de colaboración de documentos (tipo Google Docs)
    • Hospital con acceso a historiales según departamento, turno y emergencia
    • Aplicación empresarial con 20 departamentos diferentes
  4. Implementación: Diseña la política de acceso para una API REST de gestión de pedidos. Define endpoints, roles, y permisos usando formato de matriz.


Laboratorio práctico: Implementación de sistema RBAC

Tiempo estimado: 120 minutos Requisitos: Python 3.8+, pip Instalación: pip install flask pytest

Objetivo

Implementar un sistema de control de acceso basado en roles (RBAC) funcional, incluyendo gestión de usuarios, roles, permisos, y middleware de autorización.

Parte 1: Modelo de datos RBAC (25 min)

Implementa las estructuras de datos fundamentales para RBAC:

# rbac.py - Sistema RBAC
from dataclasses import dataclass, field
from typing import Set, Dict, Optional
from enum import Enum
 
class Permission(Enum):
    """Permisos disponibles en el sistema."""
    # Usuarios
    USER_CREATE = "user:create"
    USER_READ = "user:read"
    USER_UPDATE = "user:update"
    USER_DELETE = "user:delete"
 
    # Productos
    PRODUCT_CREATE = "product:create"
    PRODUCT_READ = "product:read"
    PRODUCT_UPDATE = "product:update"
    PRODUCT_DELETE = "product:delete"
 
    # Pedidos
    ORDER_CREATE = "order:create"
    ORDER_READ = "order:read"
    ORDER_READ_ALL = "order:read_all"  # Ver todos los pedidos
    ORDER_UPDATE = "order:update"
    ORDER_CANCEL = "order:cancel"
 
    # Reportes
    REPORT_SALES = "report:sales"
    REPORT_USERS = "report:users"
 
@dataclass
class Role:
    """Representa un rol con conjunto de permisos."""
    name: str
    permissions: Set[Permission] = field(default_factory=set)
    parent: Optional['Role'] = None  # Para herencia de roles
 
    def has_permission(self, permission: Permission) -> bool:
        """Verifica si el rol tiene un permiso (incluyendo herencia)."""
        if permission in self.permissions:
            return True
        if self.parent:
            return self.parent.has_permission(permission)
        return False
 
    def get_all_permissions(self) -> Set[Permission]:
        """Obtiene todos los permisos incluyendo heredados."""
        perms = self.permissions.copy()
        if self.parent:
            perms.update(self.parent.get_all_permissions())
        return perms
 
@dataclass
class User:
    """Representa un usuario del sistema."""
    id: str
    username: str
    roles: Set[Role] = field(default_factory=set)
 
    def has_permission(self, permission: Permission) -> bool:
        """Verifica si el usuario tiene un permiso a través de sus roles."""
        return any(role.has_permission(permission) for role in self.roles)
 
    def get_all_permissions(self) -> Set[Permission]:
        """Obtiene todos los permisos del usuario."""
        perms = set()
        for role in self.roles:
            perms.update(role.get_all_permissions())
        return perms

Ejercicio 1.1: Extiende el modelo para soportar:

  • Permisos por recurso específico (ej: order:123:read)
  • Restricciones temporales (rol válido solo en cierto horario)

Parte 2: Definición de roles del sistema (20 min)

Crea la configuración de roles para un sistema de e-commerce:

# roles_config.py - Configuración de roles
from rbac import Role, Permission
 
def crear_roles() -> Dict[str, Role]:
    """Define la jerarquía de roles del sistema."""
 
    # Rol base: sin permisos
    guest = Role(name="guest")
 
    # Cliente: puede ver productos y gestionar sus pedidos
    customer = Role(
        name="customer",
        permissions={
            Permission.PRODUCT_READ,
            Permission.ORDER_CREATE,
            Permission.ORDER_READ,      # Solo sus propios pedidos
            Permission.ORDER_CANCEL,    # Solo sus propios pedidos
        },
        parent=guest
    )
 
    # Vendedor: gestiona productos y ve todos los pedidos
    seller = Role(
        name="seller",
        permissions={
            Permission.PRODUCT_CREATE,
            Permission.PRODUCT_UPDATE,
            Permission.ORDER_READ_ALL,
            Permission.ORDER_UPDATE,
        },
        parent=customer
    )
 
    # Administrador: acceso total
    admin = Role(
        name="admin",
        permissions={
            Permission.USER_CREATE,
            Permission.USER_READ,
            Permission.USER_UPDATE,
            Permission.USER_DELETE,
            Permission.PRODUCT_DELETE,
            Permission.REPORT_SALES,
            Permission.REPORT_USERS,
        },
        parent=seller
    )
 
    return {
        "guest": guest,
        "customer": customer,
        "seller": seller,
        "admin": admin,
    }
 
# Ejercicio: Agrega un rol "auditor" que pueda:
# - Leer todos los recursos (usuarios, productos, pedidos)
# - Ver todos los reportes
# - NO puede modificar nada

Parte 3: Middleware de autorización (35 min)

Implementa el middleware que verifica permisos en cada request:

# middleware.py - Middleware de autorización
from functools import wraps
from flask import Flask, request, jsonify, g
from rbac import User, Permission
from typing import Callable, List
 
app = Flask(__name__)
 
# Simulación de base de datos de usuarios
users_db: Dict[str, User] = {}
roles = crear_roles()
 
class AuthorizationError(Exception):
    """Error de autorización."""
    pass
 
def get_current_user() -> Optional[User]:
    """Obtiene el usuario actual del request (simplificado)."""
    user_id = request.headers.get('X-User-ID')
    return users_db.get(user_id)
 
def require_permission(*required_permissions: Permission):
    """Decorador que verifica permisos antes de ejecutar la función."""
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            user = get_current_user()
 
            if not user:
                return jsonify({"error": "No autenticado"}), 401
 
            # Verificar cada permiso requerido
            for permission in required_permissions:
                if not user.has_permission(permission):
                    # Log del intento de acceso denegado
                    log_access_denied(user, permission, request.path)
                    return jsonify({
                        "error": "Acceso denegado",
                        "required": permission.value,
                        "user_permissions": [p.value for p in user.get_all_permissions()]
                    }), 403
 
            # Log del acceso permitido
            log_access_granted(user, required_permissions, request.path)
            return func(*args, **kwargs)
        return wrapper
    return decorator
 
def log_access_denied(user: User, permission: Permission, path: str):
    """Registra intentos de acceso denegado para auditoría."""
    print(f"[DENIED] User={user.username}, Permission={permission.value}, Path={path}")
 
def log_access_granted(user: User, permissions: tuple, path: str):
    """Registra accesos permitidos para auditoría."""
    perms = [p.value for p in permissions]
    print(f"[GRANTED] User={user.username}, Permissions={perms}, Path={path}")
 
# Ejemplo de endpoints protegidos
@app.route('/api/products', methods=['GET'])
@require_permission(Permission.PRODUCT_READ)
def list_products():
    return jsonify({"products": ["Laptop", "Mouse", "Teclado"]})
 
@app.route('/api/products', methods=['POST'])
@require_permission(Permission.PRODUCT_CREATE)
def create_product():
    return jsonify({"message": "Producto creado"}), 201
 
@app.route('/api/orders', methods=['GET'])
@require_permission(Permission.ORDER_READ_ALL)
def list_all_orders():
    return jsonify({"orders": [{"id": 1}, {"id": 2}]})
 
@app.route('/api/reports/sales', methods=['GET'])
@require_permission(Permission.REPORT_SALES)
def sales_report():
    return jsonify({"total_sales": 150000})

Ejercicio 3.1: Implementa verificación de propiedad de recursos:

def require_ownership_or_permission(
    resource_type: str,
    permission: Permission
):
    """
    Permite acceso si:
    1. El usuario es dueño del recurso, O
    2. El usuario tiene el permiso especificado
 
    Ejemplo: Un cliente puede ver SU pedido, o un admin puede ver CUALQUIER pedido.
    """
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            user = get_current_user()
            resource_id = kwargs.get(f'{resource_type}_id')
 
            # Tu implementación aquí:
            # 1. Verificar si el usuario es dueño del recurso
            # 2. Si no es dueño, verificar si tiene el permiso
            # 3. Denegar si ninguna condición se cumple
 
            pass
        return wrapper
    return decorator

Parte 4: Pruebas del sistema RBAC (25 min)

Escribe tests para verificar que el sistema funciona correctamente:

# test_rbac.py - Pruebas del sistema RBAC
import pytest
from rbac import User, Role, Permission
from roles_config import crear_roles
 
@pytest.fixture
def roles():
    return crear_roles()
 
@pytest.fixture
def customer_user(roles):
    user = User(id="1", username="cliente@test.com")
    user.roles.add(roles["customer"])
    return user
 
@pytest.fixture
def admin_user(roles):
    user = User(id="2", username="admin@test.com")
    user.roles.add(roles["admin"])
    return user
 
class TestRolePermissions:
    """Tests para verificar permisos de roles."""
 
    def test_customer_can_read_products(self, customer_user):
        assert customer_user.has_permission(Permission.PRODUCT_READ)
 
    def test_customer_cannot_delete_products(self, customer_user):
        assert not customer_user.has_permission(Permission.PRODUCT_DELETE)
 
    def test_customer_can_create_orders(self, customer_user):
        assert customer_user.has_permission(Permission.ORDER_CREATE)
 
    def test_customer_cannot_read_all_orders(self, customer_user):
        assert not customer_user.has_permission(Permission.ORDER_READ_ALL)
 
    def test_admin_inherits_seller_permissions(self, admin_user):
        # Admin hereda de seller, que hereda de customer
        assert admin_user.has_permission(Permission.PRODUCT_CREATE)  # De seller
        assert admin_user.has_permission(Permission.ORDER_READ_ALL)   # De seller
        assert admin_user.has_permission(Permission.PRODUCT_READ)     # De customer
 
    def test_admin_has_exclusive_permissions(self, admin_user):
        assert admin_user.has_permission(Permission.USER_DELETE)
        assert admin_user.has_permission(Permission.REPORT_SALES)
 
class TestRoleHierarchy:
    """Tests para verificar herencia de roles."""
 
    def test_role_inheritance(self, roles):
        seller = roles["seller"]
 
        # Seller tiene sus propios permisos
        assert Permission.PRODUCT_CREATE in seller.permissions
 
        # Seller hereda permisos de customer
        all_perms = seller.get_all_permissions()
        assert Permission.ORDER_CREATE in all_perms  # Heredado de customer
 
    def test_permission_count(self, roles):
        admin = roles["admin"]
        all_perms = admin.get_all_permissions()
 
        # Admin debe tener todos los permisos del sistema
        # (propios + heredados de seller + heredados de customer)
        assert len(all_perms) >= 10
 
# Ejercicio: Agrega estos tests
class TestSecurityScenarios:
    """Tests para escenarios de seguridad."""
 
    def test_privilege_escalation_blocked(self, customer_user):
        """Verifica que un customer no puede escalar privilegios."""
        # Tu test aquí
        pass
 
    def test_separation_of_duties(self, roles):
        """Verifica que roles críticos no compartan permisos peligrosos."""
        # Por ejemplo: quien aprueba pagos no debería poder crearlos
        pass
 
    def test_no_orphan_permissions(self, roles):
        """Verifica que todos los permisos están asignados a al menos un rol."""
        pass

Ejecutar tests:

pytest test_rbac.py -v

Parte 5: Integración y auditoría (15 min)

Implementa logging de auditoría para cumplimiento:

# audit.py - Sistema de auditoría
import json
from datetime import datetime
from dataclasses import dataclass, asdict
from typing import Optional
 
@dataclass
class AuditEntry:
    """Entrada de auditoría para decisiones de acceso."""
    timestamp: str
    user_id: str
    username: str
    action: str
    resource: str
    decision: str  # "granted" o "denied"
    required_permissions: list
    user_permissions: list
    ip_address: Optional[str] = None
 
    def to_json(self) -> str:
        return json.dumps(asdict(self))
 
class AuditLogger:
    """Logger de auditoría para cumplimiento."""
 
    def __init__(self, log_file: str = "audit.log"):
        self.log_file = log_file
 
    def log(self, entry: AuditEntry):
        """Registra una entrada de auditoría."""
        with open(self.log_file, 'a') as f:
            f.write(entry.to_json() + '\n')
 
    def log_access_decision(
        self,
        user: User,
        action: str,
        resource: str,
        decision: str,
        required: List[Permission],
        ip: Optional[str] = None
    ):
        entry = AuditEntry(
            timestamp=datetime.utcnow().isoformat(),
            user_id=user.id,
            username=user.username,
            action=action,
            resource=resource,
            decision=decision,
            required_permissions=[p.value for p in required],
            user_permissions=[p.value for p in user.get_all_permissions()],
            ip_address=ip
        )
        self.log(entry)
 
# Uso en el middleware
audit_logger = AuditLogger()
 
# En require_permission, agregar:
# audit_logger.log_access_decision(user, request.method, request.path, "granted", required_permissions)

Entregable

Entrega un proyecto funcional que incluya:

  1. Código fuente:

    • rbac.py - Modelo de datos
    • roles_config.py - Configuración de roles
    • middleware.py - Middleware de autorización
    • audit.py - Sistema de auditoría
    • test_rbac.py - Suite de tests
  2. Documentación:

    • Diagrama de jerarquía de roles
    • Matriz de permisos por rol
    • Instrucciones de uso
  3. Tests pasando:

    pytest test_rbac.py -v --tb=short
    # Todos los tests deben pasar
  4. Demostración:

    • Script o Jupyter notebook mostrando:
      • Creación de usuarios con diferentes roles
      • Intentos de acceso permitidos y denegados
      • Logs de auditoría generados

Criterios de evaluación

CriterioPuntos
Modelo de datos correcto20
Herencia de roles funcional15
Middleware de autorización25
Tests completos20
Sistema de auditoría10
Documentación y código limpio10

Reflexión

Después de completar el laboratorio:

  • ¿Por qué es importante verificar permisos en el servidor y no solo en el frontend?
  • ¿Cómo manejarías una situación donde necesitas revocar inmediatamente los permisos de un usuario?
  • ¿Qué pasaría si un atacante logra modificar la tabla de roles en la base de datos?

Navegación:Anterior | Inicio | Siguiente: Evaluación