
Probablemente ya estés familiarizado con términos como Sandboxing, Hardened Runtime y Notarización. Después de todo, estas son capacidades requeridas si tienes previsto distribuir tus apps macOS a través de la Mac App Store. De igual modo, y a partir de macOS Sequoia 15, Apple ha reforzado incluso más las protecciones relacionadas con la seguridad de las apps al ser ejecutadas. Por ejemplo, era bastante común el poder utilizar COntrol + Clic sobre cualquier app macOS descargada de internet, y que no estuviese firmada, para simplemente seleccionar la opción Abrir en el menú contextual y así ejecutarla. Eso ya no es posible hacerlo de forma tan sencilla en macOS Sequoia.
De hecho, Apple recomienda Notarizar el software incluso si tienes pensado distribuirlo desde tu propio sitio web, fuera de la Mac App Store. Pero, ¡no te preocupes! Actualmente existen múltiples opciones (más allá de Xcode) que te permiten realizar esta tarea, incluso si las apps han sido creadas y compiladas con Xojo. A lo largo de este artículo verás como activar el Sanboxing, el Hardened Runtime e incluso Notarizar sobre una sencilla aplicación de ejemplo. Por supuesto, este artículo sólo se centra en los fundamentos, y depende de ti leer la documentación relacionada en el sitio web de Apple para añadir las entradas, tanto los correspondientes a los Entitlements como a las entradas adicionales de clave/valor para el archivo Info.plist, requeridos en el caso de tu app en concreto; como por ejemplo el acceso a archivos, acceso a la cámara o micrófono, acceso de red, etc.
Un poco de Tierra en Común
Si estás utilizando Xojo 2024r4 o posterior, comprobarás que la aplicación de Sandboxing, Hardened Runtime y la Notarización están integrados en el IDE; pero en el caso de que estés utilizando una versión anterior de Xojo (o bien cualquier otro entorno de desarrollo), entonces a continuación verás como podrás hacer dichas operaciones mediante la ejecución de diferentes comandos desde el Terminal.
Puede que en este momento tu cabeza esté dando vueltas en el caso de que no estés familiarizado con estos términos relacionado con la seguridad de las apps; así que, ¿a qué se refieren términos como Sandbox, Hardened Runtime y Notarización cuando se aplican a las apps de macOS?
Sandboxing
Cuando una app de macOS tiene activado el Sandboxing esto significa que macOS creará un contenedor exclusivo para todo lo relacionado con la app durante su primera ejecución. Esto es lo que también ocurre, por ejemplo, cada vez que instalamos una app de iOS. Dicho contenedor tendrá su propia estructura de carpetas para accede a elementos como los Documentos, Imágenes, Descargas, etc. Piensa en ello como el espacio de ejecución privado para la app:

Por supuesto, te estarán esperando algunos entitlements requeridos para que tu app con Sandblxing aplicado pueda acceder a los archivos creados por otras apps (incluyendo el Escritorio, la carpeta de Descargas, Películas, Música, imágenes, etc), entre otras cosas.
Hardened Runtime
Cuando se activa para tu app macOS, el Hardened Runtime añade una capa extra de protección al código en ejecución propiamente dicho. Por ejemplo, evita cierta clase de explouts, como la inyección de código, el secuestro de biblioteca vinculada dinámicamente (DLL) y la manipulación del espacio de memoria del proceso. Este tipo de protección también se mejora mediante la Protección de Integridad del Sistema en macOS (SIP).
Notarización
En resumen, se trata de una tercera capa de confianza para los potenciales usuarios de tu app macOS. Cuando la app está notarizada, esto asegura que el software firmado con un Identificador de Desarrollador determinado ha sido comprobado por Apple en busca de componentes maliciosos. No está relacionado con el proceso de revisión realizado por Apple cuando se envía la app para su distribución a la Mac app Store, sino con la tecnología Gatekeeper de macOS. Por tanto, cuando una app notarizada se descarga de Internet, Gatekeeper utilizará el ticket de notarización asociado con la app o archivo DMG para proporcionar una información más significativa sobre el origen de la app, incluyendo el hecho de si es seguro o no su ejecución.

Preparación
Para poder seguir este artículo necesitaras:
- Xojo. Descárgalo para macOS si no lo has hecho ya; en el caso de que quieras crear y aplicar este proceso para aquellas apps nativas creadas con Xojo. (Aunque también se puede aplicar sobre los bundle de aplicación creados mediante otras herramientas).
- macOS 11.3 o posterior.
- Xcode 13 o posterior. Ejecútalo al menos una vez para asegurarte de que están instalados todos los SDK y componentes requeridos.
- Apple Developer ID. Es preciso que se disponga de una membresía de pago en el programa Apple Developer. También has de asegurarte de que los certificados estén instalados en tu mac (puede realizarse desde el propio Xcode).
- Conexión a Internet.
Con todos estos requisitos cumplidos, abre Xojo para crear un proyecto Desktop para macOS y crea un diseño básico en la ventana por omisión. No es preciso que la app ofrezca ninguna funcionalidad en absoluto para mantener nuestro foco en el tema que estamos tratando. Luego, utiliza Build Settings > macOS > Mac App Name para proporcionar un nombre a la aplicación cuando se compile (para este ejemplo utilizaremos «SandboxedApp»). Si compilas la app de macOS con otra herramienta, el resultado ha de ser un bundle estándar de app para macOS.

Por último, guarda el proyecto desde el IDE de Xojo (por ejemplo en la carpeta Documentos), y haz clic en el boton Build para compilar la app. En este punto no se requiere la asignación del valor Developer ID que debería de introducirse en la sección Build Settings > macOS > Sign, dado que la firmaremos (de nuevo) en los siguientes pasos.
Crear el archivo de Entitlements
El archivo de entitlements es muy similar al archivo Info.plist que probablemente ya conozcas, encargado de contener los pares de clave/valor para que la app funcione correctamente. El contenido de ambos archivos está en formato XML, y la única diferencia es que, mientras que el archivo Info.plist está generado automáticamente por Xojo, en el caso del archivo Entitlements deberás de crearlo manualmente tu utilizando tu editor de textos preferido.
Por tanto, abre el editor de textos (existen muchas opciones, tanto gratuitas como de pago; personalmente tiendo a utilizar BBEdit de BareBones Software). Añade las siguientes líneas de código en el documento de texto y guárdalo con el nombre «Entitlements.plist» (si lo guardas en la misma carpeta que la utilizada a la hora de compilar tu app macOS, mejor). Este es el archivo en el que probablemente querrás añadir más entradas en función de los requerimientos específicos de tu app:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> <plist version="0.9"> <dict> <key>com.apple.security.app-sandbox</key> <true/> </dict> </plist>
Aplica Sandboxing a tu App
Con la app compilada y el archivo de entitlements ya creado, abre una ventana del Terminal y escribe el siguiente comando, pulsando a continuación la tecla retorno:
> codesign --force --deep --timestamp --entitlements <ruta-al archivo-entitlements.plist> -s "Developer ID Application: <tu-nombre-completo-de-desarrollador (incluyendo-el-id-de-equipo)>" <ruta-al-bundle-de-tu-app>
Una vez ejecutado el comando, ejecuta la app «SanboxedApp», abre la app Monitor de Actividad y asegúrate de que la opción Sandbox esté activada en las opciones del menú Ver > Columnas. Luego, utiliza el campo de búsquedas de la ventana principal para filtrar los procesos mostrados de modo que sólo se liste tu app. Echa un vistazo al valor bajo la columna Sandbox y verás que la app se muestra ahora con el Sandboxing aplicado. También puedes comprobar que se ha creado un Contendor para la app en la ruta Library/Containers. Sal de la app de ejemplo cuando estés listo.

Hardened Runtime
Con nuestra app con el Sandboxing ya aplicado, veamos ahora como aplicarle la opción de Hardener Runtime. Nuevamente, escribe el siguiente comando en la línea de comandos del Terminal:
> codesign --force --deep --options runtime --timestamp --entitlements <ruta-a-tu-archivo-entitlements.plist -s "Developer ID Application: <tu-nombre-completo-de-desarrollador (incluyendo-el-id-de-equipo)>" <ruta-al-bundle-de-tu-app>
Como puedes ver, no varía mucho con respecto al anterior comando. Todo lo que añade es el texto «-options runtime» responsable de activar el Hardened Runtime. Como puedes imaginar, este comando también activa el Sandboxing de la app al tiempo que activa el Hardened Runtime, ambas cosas a la vez.
¿Quieres comprobar si ha funcionado correctamente? Bien, escribe el siguiente comando en el Terminal:
> codesign --display --verbose <ruta-al-bundle-de-tu-app>
Producirá una salida similar a la siguiente:
Executable=<ruta-al-bundle-de-tu-app> Identifier=com.xojo.sandboxedapp Format=app bundle with Mach-O universal (x86_64 arm64) CodeDirectory v=20500 size=43297 flags=0x10000(runtime) hashes=1342+7 location=embedded Signature size=9100 Timestamp=13 Aug 2024 at 12:51:28 PM Info.plist entries=15 TeamIdentifier=************ Runtime Version=11.1.0 Sealed Resources version=2 rules=13 files=4 Internal requirements count=1 size=184
Es específicamente el texto “flags=0x1000(runtime)” el que nos indica que, de hecho, la app cuenta con el Hardened Runtime activado. ¡Enhorabuena!
Notarizar la App
Se trata del último paso, pero va a requerir de un paso adicional por nuestra parte. Dado que la línea de comandos notarytool, empleada para notarizar la app, requiere del uso del ID y la contraseña correspondientes a tu cuenta Apple ID, sumado al hecho que utiliza la autenticación 2FA, resulta muy conveniente crear una contraseña específica de aplicación para ello.
Crear una Contraseña Específica de Aplicación
Sigue estos pasos para crear la contraseña utilizada por el proceso notarytool:
- Accede a appleid.apple.com
- En la sección Sign-in and Security, selecciona la opción App-Specific Passwords:

- La anterior acción mostrará un nuevo diálogo en el que se listarán todas las contraseñas específicas de aplicación que ya hubieses creado. Haz clic en el botón «+» para añadir una nueva contraseña:

- Escribe un nombre significativo como el «Título» o descripción para tu nueva contraseña en el diálogo presentado (notarytool podrá ser uno bastante bueno):

- Una vez que hagas clic en el botón Create es posible que debas de autenticarte de nuevo utilizando tu Apple ID para ello. Una vez realizado, se presentará un nuevo diálogo con la contraseña generada. Cópiala y escríbela o pégala en un lugar seguro, dado que será la que necesitemos en el siguiente paso.
Añadir al Llavero la contraseña específica de notarytool
Dado que esta contraseña específica de aplicación se utilizará por la utilidad notarytool en la línea de comandos, será más que conveniente almacenarla en el Llavero de macOS. Para ello, escribe el siguiente comando en el Terminal y pulsa la tecla Retorno:
> xcrun notarytool store-credentials "notarytool-password" --apple-id "<tu-Apple-ID>" --team-id <tu-team-id> --password <la-contraseña-copiada-en-el-paso-anterior>
Una vez ejecutado podrás ver la contraseña añadida en la app Llavero bajo el nombre de “notarytool-password”:

Crear un archivo Zip para la app
El proceso de notarización es realizado por el servicio de notarización de Apple ejecutado en sus servidores; esto es, en Internet; lo que significa que notarytool necesita enviar (subir) el bundle de tu app en un formato de archivo apropiado. Existen dos opciones: como archivo DMG (que ha de ser firmado antes de subirlo), o bien como archivo Zip, que es más rápido y también sencillo (¿sabes cuán sencillo es crear archivos Zip en el lenguaje de programación Xojo?).
Por tanto, para poder subir nuestra app para notarizarla, hemos de crear un archivo Zip en primer lugar. Nuevamente, es momento de introducir un nuevo comando en el Terminal:
> /usr/bin/ditto -c -k --keepParent <ruta-al-bundle-de-tu-app> <ruta-al-archivo-donde-guardar-el-zip/nombre-de-archivo.zip>
Subir la app para Notarizar
Con el archivo Zip ya creado, ya tenemos todas la piezas para enviarla al proceso de notarización. El tiempo invertido por dicho proceso puede variar (y lo hará) atendiendo a diversos factores.
Para enviar el archivo, escribe el siguiente comando en la ventana del Terminal:
> xcrun notarytool submit <ruta-al-archivo-donde-guardar-el-zip/nombre-de-archivo.zip> --keychain-profile "notarytool-password" --wait
Tras pulsar la tecla de Retorno comenzará el proceso y el Terminal te mostrará información sobre el progreso; algo similar a esto:
Conducting pre-submission checks for <nombre-de-tu-archivo-zip> and initiating connection to the Apple notary service... Submission ID received id: <some-id-number-goes-here> Upload progress: 100.00% (8.65 MB of 8.65 MB) Successfully uploaded file id: <some-id-number-goes-here> path: <path-of-the-zip-file> Waiting for processing to complete. Current status: Accepted........ Processing complete id: <guarda-este-id-en-un-lugar-seguro-lo-necesitarás-luego> status: Accepted
¿Has observado la última línea? El texto «status: Accepted» significa que todo ha funcionado correctamente y que el proceso de notarización ha tenido éxito; pero, ¡mejor si lo comprobamos! Escribe el siguiente comando en la ventana del Terminal. Este indicará a la herramienta notarytool que descargue el archivo de registro en formato JSON y que lo guarde en la ruta indicada. Es un buen hábito hacerlo, ya que dicho archivo de registro también incluirá cualquier error eventual y la explicación sobre dichos posibles errores encontrados durante el proceso de notarización, incluyendo aquellos relacionados con la app propiamente dicha:
> xcrun notarytool log <escribe-aqui-el-valor-guardado-en-lugar-seguro-correspondiente-al-campo-id-de-la-salida-anterior> --keychain-profile "notarytool-password" <ruta-para-guardar-el-registro.json>
¡Grapa el Ticket!
Asumiendo que todo haya funcionado correctamente, es el momento de «grapar» el ticket de notarización sobre la app propiamente dicha. No es requerido, pero sí conveniente para evitar posteriores comprobaciones en línea cuando el usuario ejecute la app o Gatekeeper la inspeccione.
Lo has adivinado, esto significa ejecutar un nuevo comando desde la ventana del Terminal sobre la app en la que ya se ha aplicado Sandboxing y Hardened Runtime (no sobre el archivo Zip que has creado y enviado utilizando notarytool):
> xcrun stapler staple "<ruta-al-bundle-de-la-app-firmada-con-sandboxing-y-hardened-runtime>"
Tras esto, puedes comprobar que todo ha funcionado correctamente utilizando el siguiente comando:
> spctl -a -vvv -t install <ruta-al-bundle-de-la-app-firmada-con-sandboxing-y-hardened-runtime>
Y verás una salida similar a la siguiente:
source=Notarized Developer ID origin=<el-developer-id-completo>
Distribución de la App
Correcto, pero probablemente querrás distribuir tu app en Internet utilizando un contenedor DMG. En este caso, sigue estos pasos:
- Crea un archivo (container) DMG.
- Copia el bundle de la app ya notarizada en el DMG.
- Notariza el archivo DMG.
- Grapa el ticket sobre el archivo DMG.
De esa forma el contenedor DMG estará notarizado junto con el bundle de la app que contiene.
Conclusiones
Como hemos visto, todo el proceso, desde la aplicación de Sandboxing, la aplicación de Hardened Runtime y la Notarización, implica una buena cantidad de comandos desde el Terminal, incluyendo la creación de un archivo Zip. Pero la buena noticia es que todo este proceso puede automatizarse en las versiones de Xojo anteriores a Xojo 2024r4, ya que a partir de dicha versión todos estos procesos están automatizados y disponibles desde el propio IDE.
Como se ha dicho al principio, este artículo sólo toca los fundamentos y no profundiza sobre la creación de los Perfiles de Aprovisionamiento (asociados con las capacidades requeridas por la app), los Entitlements que tu app pueda requerir para su correcto funcionamiento, o cualquier otro tema relacionado; de modo que probablemente encuentres de interés la siguiente documentación disponible en el portal Apple Developer:
- Perfiles de Aprovisionamiento.
- Entitlements de macOS.
- Sandbox en macOS.
- Hardened Runtime en macOS.
- Notarización en macOS.
¡Feliz Programación!