# La identidad de procesos en macOS exige más que un Team ID

Un Team ID no basta para decidir qué proceso de macOS puede usar un secreto. Identifica a un equipo de desarrollo de Apple, no a un ejecutable concreto creado por ese equipo. Si autorizas solo el Team ID, todas sus aplicaciones, auxiliares, utilidades de prueba y binarios futuros correctamente firmados pueden heredar el mismo acceso.

Una identidad de autorización defendible combina la autoridad firmante con el identificador de firma, guarda un requisito que sobrevive a actualizaciones legítimas y evalúa esa identidad contra el proceso que realmente pregunta. La ruta del ejecutable debe aparecer en la tarjeta de aprobación y el registro de auditoría, pero no decidir la autorización. Las compilaciones de desarrollo sin firma o con firma ad hoc necesitan una vía aparte y visiblemente más débil.

La diferencia importa especialmente en el límite de un secreto. Una firma indica quién firmó un proceso y qué nombre de programa declaró, pero no demuestra que merezca una contraseña de base de datos, que su petición sea sensata ni que otro programa del mismo desarrollador deba recibir el mismo permiso. La identidad es una entrada de la autorización, no su sustituto.

## El Team ID identifica al firmante, no al programa

El Team ID responde una pregunta útil pero amplia: ¿a qué equipo de desarrollo de Apple pertenece la identidad que firmó este código? En certificados emitidos por Apple, el lenguaje de requisitos expone el Team ID mediante la unidad organizativa del certificado hoja. Las herramientas también pueden mostrar `TeamIdentifier` en los detalles.

Ejecuta la inspección sobre el ejecutable exacto, no solo sobre el paquete exterior:

```sh
codesign -dvvv /Applications/Example.app/Contents/MacOS/Example 2>&1
```

La parte útil de la salida tiene esta forma:

```text
Executable=/Applications/Example.app/Contents/MacOS/Example
Identifier=dev.example.agent
Authority=Developer ID Application: Example Developer (A1B2C3D4E5)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
TeamIdentifier=A1B2C3D4E5
```

Un Team ID válido define el ámbito del firmante y excluye binarios firmados por otros equipos. Apple lo dice expresamente en SigningIdentifier: varios firmantes pueden reclamar el mismo identificador, por lo que una comprobación segura de código que no sea de Apple también necesita una restricción TeamIdentifier y una categoría de validación adecuada.

El problema inverso es que un equipo firma muchos productos y componentes. Una aplicación, su auxiliar privilegiado, un elemento de inicio, una herramienta de terminal, un servicio XPC y un diagnóstico interno pueden compartir Team ID. Un servicio de firma comprometido también podría producir otro binario dentro de ese ámbito. La pertenencia al equipo no distingue entre ellos.

Por eso `Team ID == equipo aprobado` concede demasiado para usar secretos. Es como dar acceso a toda una empresa porque las credenciales de sus empleados tienen el mismo emisor. El emisor importa, pero también el nombre de cada credencial.

Hay casos estrechos donde esa amplitud es deliberada. Una herramienta podría permitir que cualquier componente firmado por el equipo del usuario acceda a un recurso local desechable. Eso es una política de confianza en el equipo, no una identidad de proceso, y la aprobación debe decirlo. No la guardes en un campo llamado `application` para olvidar después su alcance.

## El identificador de firma separa programas del mismo equipo

El identificador de firma aporta la dimensión de programa que falta en el Team ID. `codesign` lo imprime como `Identifier`. En una aplicación suele coincidir con el identificador del paquete, pero Apple no lo exige. El firmante elige el valor, y un ejecutable de terminal puede tenerlo sin pertenecer a un paquete.

La pareja mínima mejora mucho la identidad:

```text
team_id = A1B2C3D4E5
signing_identifier = dev.example.agent
```

Significa «el programa `dev.example.agent` firmado por el equipo `A1B2C3D4E5`». Otro equipo puede copiar el identificador, pero no satisfacer la restricción de equipo. Otro programa del equipo aprobado debería tener un identificador distinto y no coincidir.

Ese «debería» importa. El equipo controla y puede reutilizar sus identificadores. Una configuración descuidada puede asignar el del programa principal a un auxiliar, y un firmante malicioso o comprometido puede copiar el valor aprobado. La pareja reduce el permiso suponiendo que el firmante protege tanto sus credenciales como su espacio de nombres.

Aun así, es el límite habitual para una identidad de terceros estable entre actualizaciones. Los hashes son más específicos, pero cambian en cada versión legítima. Las huellas de certificado tampoco son buenos anclajes porque los certificados caducan y rotan. Team ID más identificador permite aceptar versiones futuras de ese programa sin aceptar todo el catálogo del firmante.

Inspecciona cada ejecutable capaz de conectarse. No deduzcas la identidad de un auxiliar desde la aplicación contenedora. TN3127 muestra una aplicación y su herramienta integrada con el mismo Team ID y distintos identificadores, una separación que Apple recomienda. Si el auxiliar pide acceso, su identidad es la que cuenta.

Compara los identificadores byte por byte. SigningIdentifier señala que no hay normalización Unicode. Guarda el valor de Code Signing Services como cadena o bytes opacos; convertirlo a minúsculas, normalizarlo o reconstruirlo desde `CFBundleIdentifier` crea otro sistema de identidad con reglas distintas.

## El requisito designado conserva la identidad entre versiones

El requisito designado, o DR, es la expresión nativa con la que macOS decide si el código actual es el mismo que vio antes. Combina el identificador con restricciones sobre la autoridad firmante, por lo que se acerca más al objeto de autorización que cualquiera de los campos aislados.

TN3127 explica que el DR dice cómo reconocer de nuevo el código. Su prueba práctica es una actualización: la versión 1.3 debe satisfacer la identidad registrada para 1.2 aunque cambien sus bytes, y otro producto debe fallar. Un permiso duradero para secretos necesita exactamente ese equilibrio.

Muestra el DR así:

```sh
codesign -d -r- /Applications/Example.app/Contents/MacOS/Example 2>&1
```

Un resultado Developer ID suele tener esta forma, con detalles según la vía de firma:

```text
Executable=/Applications/Example.app/Contents/MacOS/Example
designated => anchor apple generic and identifier "dev.example.agent" and certificate leaf[subject.OU] = "A1B2C3D4E5"
```

No analices esa cadena para reproducir el evaluador de Apple. TN3125 advierte que las estructuras cambian y manda usar `codesign` o Code Signing Services. Obtén el requisito con `SecCodeCopyDesignatedRequirement` y evalúalo con `SecCodeCheckValidity` o las API actuales para procesos.

El DR propio del código es una afirmación, no una autorización. El firmante puede proporcionar uno explícito y el sistema puede sintetizarlo. Tu servicio decide cuál registra y si su alcance sirve para el permiso; leerlo y declarar «confiable» confunde material de identidad con política.

Para software Developer ID normal, guarda durante la aprobación el DR validado y conserva Team ID e identificador como datos legibles de auditoría. Después evalúa el proceso vivo contra el requisito guardado, no compares textos impresos. Los requisitos describen comportamiento y dos expresiones equivalentes no necesitan el mismo formato.

Los cambios de distribución requieren una decisión. TN3127 señala que los DR predeterminados de Mac App Store y Developer ID no son compatibles automáticamente, aunque Xcode puede crear requisitos compatibles. No ensanches el verificador al cambiar de canal: trata el nuevo requisito como cambio de identidad y pide aprobación, salvo que hayas diseñado y probado una compatibilidad explícita.

## La ruta aporta contexto, no una prueba

La ruta dice dónde encontró macOS la imagen. Ayuda mucho en una aprobación, pero demuestra poco sobre continuidad. Los archivos se mueven, la translocación cambia ubicaciones, los usuarios conservan versiones y los gestores instalan en rutas versionadas. Un atacante que sustituya un archivo en una ruta escribible aprobada heredará un permiso basado solo en esa ruta.

Una lista de rutas falla en ambos sentidos: rechaza el mismo programa tras moverlo y acepta bytes distintos tras una sustitución. Revisar propietario y permisos reduce algunos riesgos, pero no convierte un nombre de archivo en identidad criptográfica.

Conserva la ruta para tres tareas:

- Mostrar qué instalación inició la petición.
- Registrar contexto para investigar una llamada extraña.
- Aplicar una restricción de ubicación después de validar la firma.

El último uso puede ser razonable en sistemas gestionados. Puedes exigir un DR aprobado y una ubicación bajo un directorio propiedad de root. La ruta limita dónde se ejecuta un programa ya identificado; nunca repara una firma ausente o inválida.

Registra el ejecutable asociado al proceso real. No confíes en `argv[0]`, el directorio de trabajo, una ruta enviada en la solicitud ni una variable de entorno: son afirmaciones del solicitante. Incluso una ruta canónica puede sufrir una carrera si inspeccionas un archivo y luego autorizas un proceso por el nombre guardado.

Guardo la ruta observada y la resuelta cuando el sistema las ofrece, porque los enlaces y envoltorios explican sorpresas. Ninguna entra en la tupla de identidad duradera. Si hay restricción, ponla en `location_constraint` para que quede claro que es política adicional.

## Evalúa al proceso vivo y no a un nombre de ruta

La comprobación debe ligarse al proceso que conecta. Revisar un archivo al instalarlo y confiar luego en su ruta deja un intervalo de sustitución. Revisar el archivo actual después de recibir la solicitud también puede mirar un reemplazo y no la imagen que ya se ejecuta.

Code Signing Services distingue código estático en disco y código dinámico de un proceso. Apple documenta `SecCodeCopyGuestWithAttributes` para obtener el objeto de un huésped, normalmente por PID, y `SecCodeCheckValidityWithProcessRequirement` para evaluar un proceso. Las API ligeras permiten expresar TeamIdentifier y SigningIdentifier. Usa la API admitida para tu versión, no analices la salida de `codesign` en producción.

Un flujo fiable es:

1. Obtén la identidad que el núcleo adjunta al canal IPC, como el token de auditoría; no aceptes un PID del cuerpo.
2. Resuelve ese proceso vivo a código dinámico y valida su firma.
3. Evalúa el requisito o las restricciones guardadas contra ese mismo objeto.
4. Registra ruta, PID, identidad de inicio cuando exista, datos de firma y resultado.
5. Liga la aprobación a la conexión o vida del proceso y elimínala al terminar.

Un PID aislado no es estable porque el núcleo lo reutiliza. Si esperas entre leerlo y resolverlo, puedes inspeccionar otro proceso. Un token de auditoría contiene más identidad y verificar por conexión reduce la ventana. XPC, sockets Unix y otros IPC ofrecen API distintas, pero el sujeto siempre debe venir de la visión del sistema operativo.

Valida antes de mostrar. De otro modo, una tarjeta podría presentar campos de una firma inválida como avalados por macOS. Distingue «firma Developer ID válida» de «había texto de identificador» y no retrocedas a una ruta si falla la validación.

Decide también qué ocurre con el árbol de procesos. Si un agente firmado lanza `/bin/sh` y el shell conecta, el shell es el par. Subir a un padre más cómodo puede autorizar hijos sustituidos. Si quieres autorizar al lanzador, liga una capacidad a su conexión autenticada y pásala por un canal controlado; no adivines intención recorriendo PID padres.

La aprobación de sesión debe caducar cuando salga el proceso. La identidad guardada puede facilitar aprobaciones futuras, pero un permiso de ejecución no debe pegarse para siempre a cualquier coincidencia. Separa «reconocemos este programa» de «aprobamos esta ejecución».

## Las compilaciones sin firma necesitan otra política

El código sin firmar no tiene DR. El código con firma ad hoc tiene uno ligado a esa versión y cambia al recompilar. TN3127 dice que macOS no puede seguir de forma fiable ninguna de las dos clases entre versiones. Un intermediario de secretos debe conservar esa limitación.

Para secretos de producción, deniega si falta una firma válida y estable. Es la regla más clara. Los desarrolladores pueden firmar compilaciones locales con Apple Development o con una identidad privada administrada por la organización. La molestia suele ser menor que la ambigüedad de una excepción permanente.

El desarrollo local puede necesitar un modo débil. Hazlo voluntario, llámalo `aprobación de desarrollo` y limita su alcance: vida del proceso y hash exacto, aviso visible de `sin firma` o `ad hoc`, sin secretos de producción y nueva aprobación tras recompilar. La repetición refleja que macOS ya no puede demostrar continuidad.

No uses estos sustitutos:

- Una ruta en el directorio del usuario, que ese usuario puede reemplazar.
- Un nombre o identificador leído de metadatos que cualquiera puede declarar.
- La firma del padre, porque el hijo es quien usa el permiso.
- Una aprobación general del terminal, que ejecuta cualquier programa.
- Un hash como identidad permanente, que cambia en cada compilación legítima.

El hash sí puede estrechar una excepción temporal. Significa «estos bytes en esta ejecución», no «el mismo programa tras actualizar». Haz visible la diferencia en almacenamiento e interfaz.

Clasifica un Team ID ausente, no permitas que un valor nulo omita la comparación. Código de Apple, firma independiente, ad hoc y sin firma no caben en una sola tupla. Define categorías aceptadas y prueba cada una; la rama permisiva de `if team != expected` merece una prueba propia.

## Guarda un registro que explique su alcance

El registro duradero debe contener el requisito evaluable, hechos legibles, categoría, restricción de ubicación y alcance. Quien lo revise meses después debe distinguir entre una ejecución, versiones futuras y todos los programas de un equipo.

El ejemplo usa valores ficticios y representa en base64 un requisito serializado. El blob debe proceder de Code Signing Services, no de una cadena enviada por el cliente:

```json
{
  "schema": 1,
  "code_category": "developer_id",
  "team_id": "A1B2C3D4E5",
  "signing_identifier": "dev.example.agent",
  "designated_requirement": "BASE64_PLATFORM_REQUIREMENT",
  "approval_scope": "matching_identity_per_session",
  "location_constraint": null,
  "observed_path": "/Applications/Example.app/Contents/MacOS/Example"
}
```

El algoritmo debe ser sencillo: clasifica y valida al llamante vivo, evalúa el requisito, confirma que Team ID e identificador coinciden con los campos de auditoría, aplica ubicación y consulta alcance y aprobaciones por secreto. Una discrepancia indica estado corrupto o un error, no una razón para ampliar acceso.

El pseudocódigo deja claro el fallo:

```text
caller = peer_from_kernel(connection)
code = dynamic_code(caller)
result = validate(code)

if result.category not in accepted_categories:
    deny("unsupported code category")

identity = evaluate(stored_requirement, code)
if identity != satisfied:
    deny("process identity changed")

if facts(code) != stored_audit_facts:
    deny("identity record inconsistent")

if location_constraint and not location_allowed(code, location_constraint):
    deny("approved program ran from an unapproved location")

authorize_for(connection_lifetime, requested_secret)
```

No acepta solo Team ID, no compara ruta antes de firma, no busca un ancestro más favorable ni convierte en silencio un llamante sin firma. Cada fallo ofrece una razón comprensible.

Trata la migración como operación. Una transferencia de equipo, cambio de identificador o canal, o paso de ad hoc a Developer ID puede cambiar legítimamente el requisito. Muestra datos viejos y nuevos, exige autorización y conserva ambos en auditoría. No sobrescribas el pasado.

Prueba casos hostiles: mismo equipo con dos identificadores, mismo identificador con otro equipo, copia a otra ruta, sustitución tras iniciar y recompilación ad hoc. Solo la identidad prevista debe pasar y el permiso temporal no debe sobrevivir.

## La identidad no decide si la acción está permitida

Una coincidencia solo identifica al sujeto. No permite todos los secretos, hosts ni duraciones. Mantén separadas identidad de proceso, aprobación de sesión y aprobación de acción aunque una pantalla las reúna.

Así evitas que una aprobación para un token de desarrollo se convierta después en acceso a todas las credenciales. Team ID, identificador y DR no contienen el límite de recurso original; la identidad puede seguir estable mientras la autorización crece en silencio.

Expresa sujeto, acción, recurso, condiciones y duración:

```text
subject: requirement R42 satisfied by this live process
action: perform an HTTP request with injected bearer credentials
resource: issue-tracker-development
conditions: vault unlocked and session approved
lifetime: this connection, with per-call approval if the secret requires it
```

El sujeto referencia la identidad; los demás campos proceden de la operación y controles. Así un agente puede usar HTTP y SSH sin asumir que reconocerlo autoriza ambos canales.

Los derechos firmados solo deben intervenir si la política les da un significado preciso. Su presencia no vuelve seguro al programa ni su ausencia debilita la pareja Team ID e identificador.

La notarización y Gatekeeper responden otras preguntas. Pueden informar la categoría y confianza de distribución, pero no distinguen un programa dentro del equipo ni conceden secretos. `spctl -a -vv -t exec` ayuda a diagnosticar aceptación, no sustituye el requisito guardado.

La revocación también se separa: revocar sesión termina la conexión, revocar identidad fuerza otra aprobación y revocar un recurso conserva los demás. Un booleano `trusted` no puede expresar esas decisiones.

Con esos límites, un auditor puede demostrar qué programa firmado se ejecutó, quién aprobó la ejecución y qué operación recibió. Sin los tres registros, incluso un DR perfecto deja sin respuesta qué permitió reconocer el proceso.

## La aprobación debe decir qué se demostró

Una persona no puede revisar un blob. Muestra firmante o equipo, identificador, categoría, ruta y duración. Solo encabeza con el dato más amplio si realmente concedes un permiso amplio.

No uses el nombre amigable como identidad principal: procede de metadatos mutables y puede repetirse. `Example Agent quiere acceso` no distingue versión publicada, auxiliar, recompilación local o copia sin firma.

La primera llamada de un nuevo proceso agente en Sallyport muestra una tarjeta encabezada por su autoridad de firma y liga la aprobación a esa ejecución hasta que termina. La puerta de la bóveda y el indicador por llamada siguen separados, de modo que reconocer el proceso no concede acceso ilimitado.

El evento debe guardar categoría, Team ID, identificador, referencia al requisito, ruta, proceso, sesión, decisión, motivo y alcance. El revisor futuro no debería necesitar el archivo actual en la antigua ruta.

No llames al resultado `proceso confiable`. Lo demostrado es más estrecho: el proceso vivo satisfizo un requisito y una persona o política le concedió una acción definida. Esa frase frena la expansión cuando aparecen otros secretos y agentes.

El Team ID participa, pero no basta. Nombra al firmante con Team ID, al programa con su identificador, conserva continuidad mediante el DR y liga la prueba al solicitante vivo. Si falta una pieza, estrecha el permiso o vuelve a preguntar. Un límite de secretos debe mostrar la incertidumbre, no convertirla en acceso permanente.
