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:
- Identificación: ¿Quién eres? (username, certificado)
- Autenticación: ¿Cómo lo pruebas? (contraseña, MFA)
- 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
| Contexto | Aplicación del principio |
|---|---|
| Usuarios | Rol base sin permisos, agregar según función |
| Servicios | Service accounts con permisos específicos |
| APIs | Scopes limitados por funcionalidad |
| Containers | Non-root, read-only filesystem donde sea posible |
| Bases de datos | Usuarios separados por aplicación, permisos por tabla |
Beneficios de seguridad
- Contención de brechas: Cuenta comprometida = daño limitado
- Reducción de superficie de ataque: Menos permisos = menos vectores
- Facilita auditoría: Permisos específicos = acciones trazables
- 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:
| Ventaja | Desventaja |
|---|---|
| Flexible para usuarios | Difícil de auditar a escala |
| Intuitivo | Propenso a over-sharing |
| Descentralizado | No 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)
| Regla | Descripción | Ejemplo |
|---|---|---|
| No Read Up | No leer datos de nivel superior | Confidencial no lee Top Secret |
| No Write Down | No escribir a nivel inferior | Top Secret no escribe a Público |
Modelo Biba (Integridad)
| Regla | Descripción | Propósito |
|---|---|---|
| No Read Down | No leer de nivel inferior | Evitar contaminación |
| No Write Up | No escribir a nivel superior | Evitar 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 propietario3. 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:
- Usuarios: Entidades que solicitan acceso
- Roles: Conjuntos de permisos que representan funciones
- Permisos: Operaciones permitidas sobre recursos
- 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 / Recurso | Usuarios | Productos | Pedidos | Reportes | Config |
|---|---|---|---|---|---|
| Admin | CRUD | CRUD | CRUD | CRUD | CRUD |
| Manager | R | CRUD | CRUD | CR | R |
| Vendedor | - | R | CR | - | - |
| Cliente | R (propio) | R | CR (propio) | - | - |
| Auditor | R | R | R | R | R |
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ística | DAC | MAC | RBAC | ABAC |
|---|---|---|---|---|
| Control | Propietario | Sistema | Admin | Políticas |
| Flexibilidad | Alta | Baja | Media | Muy alta |
| Escalabilidad | Baja | Alta | Alta | Alta |
| Complejidad | Baja | Alta | Media | Alta |
| Auditoría | Difícil | Fácil | Fácil | Media |
| Uso típico | Archivos personales | Militar/regulado | Empresas | Cloud/SaaS |
Diseño de sistemas de control de acceso
Proceso de diseño
- Identificar recursos a proteger
- Definir operaciones posibles sobre cada recurso
- Identificar actores (usuarios, sistemas, servicios)
- Mapear funciones a roles (para RBAC)
- Definir políticas de acceso
- Implementar enforcement en todos los puntos de entrada
- 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ón | Problema | Solución |
|---|---|---|
| Verificar solo en frontend | Bypass trivial | Verificar siempre en backend |
| Hardcodear permisos | Inflexible, riesgoso | Externalizar a servicio de autorización |
| Rol “superuser” compartido | Sin accountability | Roles individuales con auditoría |
| Permisos por exclusión | Difícil de auditar | Permisos 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:
| Recurso | Estudiante | Profesor | Admin_Registro | Admin_Sistema |
|---|---|---|---|---|
| Notas propias | R | R | R | R |
| Notas de curso | - | RU (sus cursos) | R | R |
| Inscripción propia | CRU | R | CRUD | CRUD |
| Inscripción otros | - | - | CRUD | R |
| Usuarios | - | - | R | CRUD |
Conceptos clave
| Término | Definición |
|---|---|
| Control de acceso | Mecanismos que determinan quién puede acceder a qué recursos |
| DAC | Modelo donde el propietario del recurso define el acceso |
| MAC | Modelo donde el sistema impone reglas de clasificación |
| RBAC | Modelo donde permisos se asignan a roles, no a usuarios |
| ABAC | Modelo basado en atributos de sujeto, recurso y contexto |
| Privilegio mínimo | Principio de otorgar solo permisos necesarios |
| ACL | Lista de permisos asociada a un recurso |
Ponte a prueba
-
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.
-
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.
-
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
-
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 permsEjercicio 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 nadaParte 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 decoratorParte 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."""
passEjecutar tests:
pytest test_rbac.py -vParte 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:
-
Código fuente:
rbac.py- Modelo de datosroles_config.py- Configuración de rolesmiddleware.py- Middleware de autorizaciónaudit.py- Sistema de auditoríatest_rbac.py- Suite de tests
-
Documentación:
- Diagrama de jerarquía de roles
- Matriz de permisos por rol
- Instrucciones de uso
-
Tests pasando:
pytest test_rbac.py -v --tb=short # Todos los tests deben pasar -
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
- Script o Jupyter notebook mostrando:
Criterios de evaluación
| Criterio | Puntos |
|---|---|
| Modelo de datos correcto | 20 |
| Herencia de roles funcional | 15 |
| Middleware de autorización | 25 |
| Tests completos | 20 |
| Sistema de auditoría | 10 |
| Documentación y código limpio | 10 |
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 →