El aislamiento de datos multiempresa en PHP no se resuelve añadiendo una condición WHERE organization_id = ? en la pantalla principal. Una fuga puede originarse en una API, una exportación, una caché, un archivo adjunto, un consumidor de cola o un proceso programado. También puede ocurrir cuando un administrador legítimo cambia de organización y el sistema conserva un contexto anterior.
El objetivo arquitectónico debe ser claro: ninguna operación que lea, modifique, procese o entregue información de cliente debe poder actuar sin un ámbito de organización verificable. Ese ámbito debe propagarse de forma explícita y validarse en cada límite relevante de la aplicación.
Qué debe aislar una aplicación SaaS

El modelo transaccional es sólo una parte de la superficie de riesgo. Inventarie los recursos que tienen propietario organizativo y defina para cada uno cómo se identifica, guarda, recupera, elimina y audita su pertenencia.
- Datos transaccionales: usuarios, proyectos, pedidos, facturas, configuraciones y relaciones entre entidades.
- Archivos y adjuntos: objetos en almacenamiento externo, miniaturas, documentos generados y sus metadatos.
- Caché: resultados de consultas, permisos calculados, sesiones, respuestas de API y datos de configuración.
- Índices de búsqueda: documentos indexados, sugerencias y filtros previamente agregados.
- Procesamiento asíncrono: trabajos de cola, reintentos, lotes de importación y notificaciones.
- Operación y observabilidad: registros, trazas, métricas, exportaciones de soporte y herramientas internas.
No todos los recursos requieren la misma estrategia. Un catálogo público puede ser compartido, mientras que una factura, su PDF y los registros de descarga deben conservar el vínculo inequívoco con la organización. La decisión debe quedar documentada para evitar que una entidad nueva nazca sin reglas de propiedad.
Elegir el modelo de aislamiento de datos
Hay tres modelos habituales. No existe uno universalmente superior: la elección depende de requisitos regulatorios, volumen, operación, modelo comercial y capacidad del equipo para mantener la plataforma.
Base de datos compartida con clave de organización
Todas las organizaciones comparten tablas y cada registro sujeto a aislamiento contiene una clave como organization_id. Es el enfoque más directo para evolucionar el producto y ejecutar consultas agregadas globales. A cambio, exige disciplina extrema: cada consulta, relación, índice, caché y tarea debe respetar el ámbito.
Como mínimo, use claves foráneas cuando proceda, índices compuestos que comiencen por organization_id y restricciones únicas también compuestas. Por ejemplo, un código de pedido único dentro de una organización no debe declararse único globalmente si esa no es la regla de negocio.
Esquema separado por organización
Cada cliente opera en un esquema lógico distinto dentro del mismo servidor de base de datos. Esto reduce el riesgo de omitir un filtro en tablas separadas, pero complica migraciones, conexiones, herramientas de análisis y consultas globales. Es apropiado sólo si el motor, el framework y la operación diaria soportan de forma consistente ese patrón.
Base de datos por organización
Separar bases de datos ofrece una frontera más fuerte y puede facilitar restauraciones o movimientos de clientes individuales. También incrementa el inventario de conexiones, migraciones, copias de seguridad, monitorización y despliegues de cambios de estructura. Conviene evaluar especialmente cómo se ejecutarán informes globales, cambios masivos y recuperación ante errores.
La separación física reduce ciertas clases de fallo, pero no sustituye autorización, control de archivos, gestión de secretos ni validación del contexto en servicios compartidos.
Arquitectura de referencia: contexto explícito en los límites
El contexto de organización no debe inferirse de parámetros arbitrarios enviados por el navegador. Debe resolverse a partir de una fuente autenticada y autorizada: un subdominio validado, un token con audiencia adecuada, una pertenencia del usuario o una credencial de integración asociada a una sola organización.
En una aplicación PHP, una capa de entrada puede construir un objeto inmutable de contexto con el identificador de organización, el actor, sus permisos y un identificador de solicitud. Los controladores, comandos de consola y consumidores de cola reciben ese contexto o lo reconstruyen mediante datos verificados. Evite variables globales mutables que puedan persistir indebidamente en procesos de larga duración.
final class OrganizationContext {
public function __construct(
public readonly string $organizationId,
public readonly string $actorId
) {}
}Los repositorios deben exigir el contexto para consultar o modificar entidades aisladas. Es preferible una interfaz que haga difícil omitirlo a una convención implícita que dependa de la memoria de cada desarrollador. Cuando sea posible, aplique además políticas de acceso en la capa de dominio: pertenecer a una organización no autoriza automáticamente cualquier acción dentro de ella.
Evitar filtros olvidados en consultas y relaciones
Una consulta aislada debe filtrar por organización antes de buscar por identificadores de negocio. Recuperar primero un registro por id y comprobar después su propietario puede producir exposiciones si el resultado se serializa, se registra o se usa antes de rechazarlo.
- Centralice las consultas en repositorios o servicios de lectura con métodos que reciban el contexto.
- Prohíba accesos directos a modelos aislados desde controladores, plantillas y consumidores de eventos.
- Revise relaciones: una relación cargada de forma diferida puede eludir el filtro aplicado a la entidad principal.
- Use restricciones de base de datos para impedir relaciones entre filas de organizaciones distintas cuando el modelo lo permita.
- Defina convenciones para migraciones, semillas de prueba y consultas analíticas.
En motores que ofrecen políticas de seguridad a nivel de fila, estas pueden aportar una defensa adicional. Sin embargo, su adopción debe incluir pruebas de conexión, gestión de roles y revisión de los procesos administrativos. No conviene asumir que una política de base de datos protege automáticamente archivos, caché o índices externos.
Riesgos fuera del flujo web principal
Los identificadores opacos reducen la enumeración, pero no autorizan acceso. Un UUID o un identificador aleatorio debe seguir resolverse dentro de la organización activa. Del mismo modo, una URL firmada de descarga necesita un objeto perteneciente al ámbito correcto, una caducidad adecuada y reglas de revocación cuando cambian permisos.
Las claves de caché deben incluir el identificador de organización y, cuando el contenido depende de permisos, una dimensión adicional de rol o versión de autorización. Una clave como dashboard:summary es insegura en un entorno multiempresa; una clave con ámbito explícito permite además invalidaciones más precisas.
Las exportaciones son especialmente sensibles porque suelen ejecutarse fuera de la solicitud original. Guarde quién la solicitó, para qué organización, qué filtros se aprobaron y dónde se entregará el resultado. No envíe adjuntos o enlaces a destinatarios calculados desde datos no validados.
Propagar el contexto en APIs, webhooks y colas
Una API debe derivar la organización de la credencial o comprobar que el recurso solicitado pertenece a la organización asociada a esa credencial. Permitir un encabezado X-Organization-Id puede ser válido para operadores con delegación explícita, pero requiere autorización específica, auditoría y una interfaz que haga visible el cambio de ámbito.
Los webhooks entrantes no deben confiar en un identificador de organización incluido en el cuerpo sin verificar firma, emisor y asociación previa de la integración. Para webhooks salientes, genere eventos desde datos ya delimitados y evite reutilizar cargas útiles desde una cola compartida sin validar el destinatario.
Cada trabajo asíncrono debe transportar un identificador de organización junto con el identificador del recurso y reconstruir el contexto antes de consultar. El consumidor debe comprobar ambos valores, incluso si el trabajo fue creado internamente. Los reintentos, trabajos diferidos y tareas programadas necesitan la misma regla: no existe un contexto de solicitud implícito disponible de forma segura.
Pruebas y señales de diagnóstico verificables
La prueba más importante no es que una organización vea sus propios datos, sino que no pueda leer ni modificar los de otra. Cree dos organizaciones con datos deliberadamente similares y ejecute pruebas de integración contra cada punto de entrada: interfaz web, API, comandos, exportaciones, descargas y consumidores de cola.
- Solicite un recurso de la organización B usando una sesión o credencial de la organización A y espere una respuesta no reveladora.
- Intente actualizar, borrar, descargar y exportar recursos cruzados, no sólo consultarlos.
- Compruebe que las claves de caché de A y B generan resultados independientes.
- Ejecute un trabajo de cola con un recurso de otra organización y verifique que falla de forma controlada.
- Pruebe restauraciones, importaciones y tareas nocturnas con datos de más de una organización.
- Registre acciones sensibles con actor, organización, recurso y resultado, sin introducir datos personales innecesarios en los registros.
Las pruebas basadas en propiedades pueden complementar los casos manuales: para cualquier recurso creado bajo una organización, ningún actor sin pertenencia válida debería poder observarlo o alterarlo mediante una ruta expuesta. Esta propiedad debe aplicarse a cambios futuros de endpoints y repositorios.
Plan de adopción para una aplicación existente
Si los datos ya están mezclados, no empiece reescribiendo toda la aplicación. Primero inventaríe entidades, flujos, integraciones y accesos administrativos. Después defina la propiedad de cada registro y resuelva los casos ambiguos con reglas de negocio revisables.
- Añada la entidad de organización y la clave de pertenencia a las tablas objetivo.
- Rellene esa clave mediante una migración controlada y conserve evidencia de los casos sin asignación fiable.
- Introduzca repositorios delimitados y pruebas de acceso cruzado en las rutas más sensibles.
- Incluya el ámbito en caché, archivos, búsquedas y trabajos nuevos.
- Migre progresivamente los flujos antiguos y bloquee nuevas consultas sin contexto en revisión de código.
- Active controles más estrictos cuando las métricas y las pruebas demuestren cobertura suficiente.
Decisiones que conviene documentar antes de crecer

Antes de incorporar la siguiente organización, documente el modelo elegido, la fuente de verdad del contexto, las excepciones de acceso administrativo, la estrategia de identificadores, los límites de caché, la propiedad de archivos, la recuperación de datos, la retención de registros y el procedimiento ante una sospecha de acceso cruzado.
También determine quién puede actuar en nombre de otra organización, cómo se aprueba esa delegación y cómo se revoca. El aislamiento de datos multiempresa en PHP se mantiene con decisiones explícitas, restricciones técnicas repetibles y pruebas que convierten una promesa de arquitectura en un comportamiento comprobable.



