13 ago 2025·8 min de lectura

Checklist de almacenamiento seguro en Kotlin para tokens, claves y PII

Checklist de almacenamiento seguro en Kotlin para elegir entre Android Keystore, EncryptedSharedPreferences y cifrado de base de datos para tokens, claves y PII.

Checklist de almacenamiento seguro en Kotlin para tokens, claves y PII

Qué intentas proteger (en términos simples)

El almacenamiento seguro en una app de negocio significa una cosa: si alguien consigue el teléfono (o los archivos de tu app), aun así no debería poder leer o reutilizar lo que guardaste. Eso incluye datos en reposo (en disco) y también fugas de secretos por backups, logs, informes de fallos o herramientas de depuración.

Una prueba mental simple: ¿qué podría hacer un extraño si abriera la carpeta de almacenamiento de tu app? En muchas apps, los elementos más valiosos no son las fotos o las configuraciones. Son cadenas pequeñas que dan acceso.

El almacenamiento en el dispositivo suele incluir tokens de sesión (para mantener a los usuarios conectados), refresh tokens, claves de API, claves de cifrado, datos personales (PII) como nombres y correos, y registros de negocio en caché usados offline (órdenes, tickets, notas de clientes).

Aquí hay modos de falla comunes en el mundo real:

  • Un dispositivo perdido o robado se examina y se copian tokens para suplantar a un usuario.
  • Malware o una app "ayudante" leen archivos locales en un dispositivo rooteado o mediante trucos de accesibilidad.
  • Backups automáticos mueven los datos de tu app a un sitio que no planeaste.
  • Builds de depuración registran tokens, los escriben en informes de fallos o desactivan comprobaciones de seguridad.

Por eso "simplemente guárdalo en SharedPreferences" no está bien para nada que conceda acceso (tokens) o pueda dañar a usuarios y empresa (PII). SharedPreferences en texto plano es como escribir secretos en una nota adhesiva dentro de la app: cómodo, y fácil de leer si alguien tiene oportunidad.

El punto de partida más útil es nombrar cada elemento guardado y hacerte dos preguntas: ¿desbloquea algo? y ¿sería un problema si se hiciera público? El resto (Keystore, preferencias encriptadas, base de datos encriptada) sigue a partir de eso.

Clasifica tus datos: tokens, claves y PII

El almacenamiento seguro se vuelve más fácil cuando dejas de tratar todos los "datos sensibles" por igual. Empieza listando lo que la app guarda y qué pasaría si se filtrara.

Tokens no son lo mismo que contraseñas. Access tokens y refresh tokens están pensados para almacenarse para mantener la sesión, pero siguen siendo secretos de alto valor. Las contraseñas no deberían almacenarse en absoluto. Si necesitas iniciar sesión, guarda solo lo necesario para mantener la sesión (normalmente tokens) y confía en el servidor para verificar contraseñas.

Claves son otra categoría. Claves de API, claves de firma y claves de cifrado pueden desbloquear sistemas enteros, no solo una cuenta de usuario. Si alguien las extrae del dispositivo, puede automatizar abusos a escala. Una buena regla: si un valor puede usarse fuera de la app para suplantarla o descifrar datos, trátalo como más riesgo que un token de usuario.

PII es cualquier cosa que pueda identificar a una persona: email, teléfono, dirección, notas de cliente, documentos de identidad, datos de salud. Incluso campos que parecen inocuos se vuelven sensibles cuando se combinan.

Un sistema de etiquetado rápido que funciona bien en la práctica:

  • Secretos de sesión: access token, refresh token, cookie de sesión
  • Secretos de app: claves de API, claves de firma, claves de cifrado (evita poner estas en dispositivos cuando sea posible)
  • Datos de usuario (PII): detalles de perfil, identificadores, documentos, información médica o financiera
  • IDs de dispositivo y analítica: advertising ID, device ID, install ID (aún sensibles según muchas políticas)

Android Keystore: cuándo usarlo

Android Keystore es ideal cuando necesitas proteger secretos que nunca deberían salir del dispositivo en forma clara. Es una caja fuerte para claves criptográficas, no una base de datos para tus datos reales.

Para qué sirve: generar y mantener claves usadas para cifrar, descifrar, firmar o verificar. Normalmente cifras un token o datos offline en otro lugar, y una clave del Keystore es lo que lo desbloquea.

Claves con soporte hardware: qué significa en realidad

En muchos dispositivos, las claves del Keystore pueden estar respaldadas por hardware. Eso significa que las operaciones con la clave ocurren dentro de un entorno protegido y el material de la clave no puede extraerse. Reduce el riesgo frente a malware que solo puede leer archivos de la app.

Que sea hardware-backed no está garantizado en todos los dispositivos, y el comportamiento varía por modelo y versión de Android. Diseña como si las operaciones con claves pudieran fallar.

Puertas de autenticación del usuario

Keystore puede exigir presencia del usuario antes de que una clave pueda usarse. Así es como ligas el acceso a biometría o credenciales del dispositivo. Por ejemplo, puedes cifrar un token de exportación y sólo descifrarlo después de que el usuario confirme con huella o PIN.

Keystore encaja bien cuando quieres una clave no exportable, cuando quieres aprobación biométrica o por credencial para acciones sensibles, y cuando quieres secretos por dispositivo que no deban sincronizarse ni viajar en backups.

Planifica los problemas: las claves pueden invalidarse tras cambios en la pantalla de bloqueo, cambios biométricos o eventos de seguridad. Espera fallos e implementa una alternativa limpia: detecta claves inválidas, borra blobs cifrados y pide al usuario iniciar sesión de nuevo.

EncryptedSharedPreferences: cuándo es suficiente

EncryptedSharedPreferences es una buena opción por defecto para un pequeño conjunto de secretos en forma clave-valor. Es "SharedPreferences, pero encriptado", de modo que nadie puede simplemente abrir un archivo y leer los valores.

En el fondo, usa una clave maestra para cifrar y descifrar valores. Esa clave maestra está protegida por Android Keystore, así que tu app no está almacenando la clave de cifrado en texto plano.

Suele ser suficiente para unos pocos elementos pequeños que lees a menudo, como access y refresh tokens, IDs de sesión, IDs de dispositivo, flags de entorno o pequeños estados como la última hora de sincronización. También sirve para trozos mínimos de datos de usuario si realmente debes guardarlos, pero no debería convertirse en un vertedero de PII.

No es una buena opción para nada grande o estructurado. Si necesitas listas offline, búsqueda o consultas por campos (clientes, tickets, órdenes), EncryptedSharedPreferences se vuelve lento e incómodo. Ahí es cuando quieres una base de datos encriptada.

Una regla simple: si puedes listar cada clave almacenada en una pantalla, probablemente EncryptedSharedPreferences está bien. Si necesitas filas y consultas, pasa a otra solución.

Cifrado de base de datos: cuándo lo necesitas

Crea herramientas internas seguras
Sustituye hojas de cálculo por un panel de administración y workflows que tu equipo mantenga sin código pesado.
Construir herramienta interna

El cifrado de base de datos importa cuando guardas más que una pequeña configuración o un token. Si tu app mantiene datos de negocio en el dispositivo, asume que pueden extraerse de un teléfono perdido a menos que los protejas.

Una base de datos tiene sentido cuando necesitas acceso offline a registros, caché local por rendimiento, historial/pistas de auditoría, o notas/adjuntos largos.

Dos enfoques comunes de cifrado

Cifrado de base de datos completa (a menudo estilo SQLCipher) cifra el archivo entero en reposo. Tu app lo abre con una clave. Es fácil de razonar porque no tienes que recordar qué columnas están protegidas.

Cifrado a nivel de aplicación por campo cifra solo ciertos campos antes de escribir y luego los descifra al leer. Esto puede funcionar si la mayoría de los registros no son sensibles, o si intentas mantener una estructura de base de datos sin cambiar el formato del archivo.

Compensaciones: confidencialidad vs búsqueda y orden

El cifrado completo oculta todo en disco, pero una vez desbloqueada la base de datos tu app puede consultar normalmente.

El cifrado por campo protege columnas específicas, pero pierdes búsqueda y ordenación sobre valores cifrados. Ordenar por un apellido cifrado no funciona bien, y buscar significa o "buscar después de descifrar" (lento) o "almacenar índices extra" (más complejidad y posibles fugas).

Fundamentos de gestión de claves

La clave de la base de datos nunca debe estar hardcodeada ni incluirse en la app. Un patrón común es generar una clave aleatoria para la base de datos y luego guardarla envuelta (cifrada) usando una clave almacenada en Android Keystore. Al hacer logout puedes borrar la clave envuelta y tratar la base de datos local como desechable, o conservarla si la app debe funcionar offline entre sesiones.

Cómo elegir: una comparación práctica

Ve más allá de los creadores simples
Ve más allá de simples creadores: crea apps nativas iOS y Android que aprovechen capacidades reales del dispositivo.
Crear móvil

No estás eligiendo "la opción más segura" en general. Estás eligiendo la opción más segura que encaje con cómo usa la app esos datos.

Preguntas que realmente guían la elección correcta:

  • ¿Con qué frecuencia se leen los datos (en cada inicio o raramente)?
  • ¿Cuánto datos son (unos pocos bytes o miles de registros)?
  • ¿Qué pasa si se filtran (molestia, coste, obligación legal de notificar)?
  • ¿Necesitas acceso offline, búsqueda o ordenación?
  • ¿Tienes requisitos de cumplimiento (retención, auditoría, reglas de cifrado)?

Un mapeo práctico:

  • Tokens (access y refresh tokens OAuth) suelen ir en EncryptedSharedPreferences porque son pequeños y se leen a menudo.
  • Material de claves debería vivir en Android Keystore siempre que sea posible para reducir la posibilidad de que se copie fuera del dispositivo.
  • PII y datos de negocio offline generalmente necesitan cifrado de base de datos cuando almacenas más de un par de campos o necesitas listas y filtros offline.

Datos mixtos son normales en apps de negocio. Un patrón práctico es generar una clave de cifrado de datos (DEK) aleatoria para tu base de datos o archivos locales, guardar solo la DEK envuelta usando una clave respaldada por Keystore y rotarla cuando sea necesario.

Si dudas, elige el camino seguro y simple: almacena menos. Evita PII offline salvo que realmente la necesites, y guarda claves en Keystore.

Paso a paso: implementar almacenamiento seguro en una app Kotlin

Empieza escribiendo cada valor que piensas guardar en el dispositivo y la razón exacta por la que debe estar allí. Esta es la manera más rápida de evitar almacenamientos "por si acaso".

Antes de escribir código, decide tus reglas: cuánto tiempo vive cada elemento, cuándo debe remplazarse y qué significa realmente "cerrar sesión". Un access token puede expirar en 15 minutos, un refresh token puede durar más y la PII offline puede requerir una regla de "eliminar tras 30 días".

Implementación que se mantiene manejable:

  • Crea un único wrapper "SecureStorage" para que el resto de la app nunca toque SharedPreferences, Keystore o la base de datos directamente.
  • Coloca cada elemento en el lugar correcto: tokens en EncryptedSharedPreferences, claves de cifrado protegidas por Android Keystore y datasets offline más grandes en una base de datos encriptada.
  • Maneja fallos a propósito. Si el almacenamiento seguro falla, falla cerrado. No caigas silenciosamente a almacenamiento en texto plano.
  • Añade diagnóstico sin filtrar datos: registra tipos de eventos y códigos de error, nunca tokens, claves o detalles de usuario.
  • Encadena rutas de borrado: logout, eliminación de cuenta y "borrar datos de la app" deben usar la misma rutina de wipe.

Luego prueba los casos aburridos que rompen el almacenamiento seguro en producción: restaurar desde un backup, actualizar desde una versión anterior, cambiar ajustes de bloqueo del dispositivo, migrar a un teléfono nuevo. Asegúrate de que los usuarios no queden en un bucle donde los datos guardados no se pueden descifrar y la app sigue intentando.

Finalmente, escribe las decisiones en una página que todo el equipo pueda seguir: qué se guarda, dónde, períodos de retención y qué debe ocurrir cuando falla el descifrado.

Errores comunes que rompen el almacenamiento seguro

Prueba tu flujo de "mal día"
Valida el almacenamiento de tokens, borrado en logout y recuperación ante fallos de claves en un pequeño POC.
Empezar un POC

La mayoría de los fallos no son elegir la librería equivocada. Ocurren cuando un atajo copia secretos en lugares que no pretendías almacenar.

La mayor señal de alarma es un refresh token (o token de sesión de larga duración) guardado en texto plano en cualquier sitio: SharedPreferences, un archivo, una caché "temporal" o una columna de base de datos. Si alguien tiene un backup, un volcado de un dispositivo rooteado o un artefacto de build de depuración, ese token puede sobrevivir a la contraseña.

Los secretos también se filtran por visibilidad, no por almacenamiento. Registrar cabeceras completas, imprimir tokens durante la depuración o adjuntar contexto "útil" a informes de fallos y eventos de analytics puede exponer credenciales fuera del dispositivo. Trata los logs como públicos.

El manejo de claves es otra laguna común. Usar una sola clave para todo aumenta el radio de daño. No rotar claves significa que compromisos antiguos siguen válidos. Incluye un plan para versionado de claves, rotación y qué pasa con datos cifrados antiguos.

No olvides las vías "fuera de la bóveda"

El cifrado no impide que los backups en la nube copien datos locales de la app. No impide capturas de pantalla o grabaciones de pantalla que capturen PII. No evita builds de depuración con ajustes relajados ni funciones de exportación (CSV/compartir) que filtran campos sensibles. El uso del portapapeles también puede filtrar códigos de un solo uso o números de cuenta.

Además, el cifrado no arregla la autorización. Si tu app muestra PII después de que un usuario cierra sesión, o mantiene cachés accesibles sin comprobación de sesión, eso es un fallo de control de acceso. Bloquea la UI, borra cachés sensibles al cerrar sesión y vuelve a comprobar permisos antes de mostrar datos protegidos.

Detalles operativos: ciclo de vida, logout y casos límite

El almacenamiento seguro no es solo dónde pones secretos. Es cómo se comportan con el tiempo: cuando la app duerme, cuando un usuario cierra sesión y cuando el dispositivo está bloqueado.

Para tokens, planifica el ciclo de vida completo. Los access tokens deben ser de corta duración. Los refresh tokens deben tratarse como contraseñas. Si un token está expirado, renuévalo silenciosamente. Si un refresh falla (revocado, contraseña cambiada, dispositivo eliminado), detén los bucles de reintento y fuerza un inicio de sesión limpio. También soporta revocación en servidor. El almacenamiento local perfecto no ayuda si nunca invalidas credenciales robadas.

Usa biometría para re-autenticación, no para todo. Pide la confirmación cuando la acción suponga riesgo real (ver PII, exportar datos, cambiar detalles de pago, mostrar una clave de un solo uso). No lo pidas en cada apertura de la app.

Al cerrar sesión, sé estricto y predecible:

  • Limpia copias en memoria primero (tokens en singletons, interceptores o ViewModels).
  • Borra tokens almacenados y estado de sesión (incluyendo refresh tokens).
  • Elimina o invalida claves de cifrado locales si tu diseño lo permite.
  • Elimina PII offline y respuestas de API en caché.
  • Desactiva jobs en background que puedan volver a descargar datos.

Los casos límite importan en apps de negocio: múltiples cuentas en un dispositivo, perfiles de trabajo, backup/restore, transferencia entre dispositivos y cierres parciales de sesión (cambiar empresa/espacio de trabajo en lugar de cerrar sesión completa). Prueba force stop, actualizaciones del SO y cambios de hora, ya que la deriva temporal puede romper la lógica de expiración.

La detección de manipulación es un compromiso. Comprobaciones básicas (builds debuggables, flags de emulador, señales simples de root, veredictos de Play Integrity) pueden reducir el abuso casual, pero atacantes decididos pueden eludirlas. Trata las señales de manipulación como entradas de riesgo: limita acceso offline, exige re-autenticación y registra el evento.

Checklist rápido antes de publicar

Convierte modelos de datos en apps reales
Modela datos en PostgreSQL de forma visual y luego genera APIs y clientes funcionales.
Empezar a crear

Usa esto antes del lanzamiento. Apunta a los lugares donde el almacenamiento seguro falla en apps de negocio reales.

  • Asume que el dispositivo puede ser hostil. Si un atacante tiene un dispositivo rooteado o una imagen completa del dispositivo, ¿puede leer tokens, claves o PII desde archivos de la app, preferencias, logs o capturas de pantalla? Si la respuesta es "tal vez", mueve secretos a protección respaldada por Keystore y mantiene el payload cifrado.
  • Revisa backups y transferencias. Mantén archivos sensibles fuera de Android Auto Backup, backups en la nube y transferencias entre dispositivos. Si perder una clave en la restauración rompería el descifrado, planifica el flujo de recuperación (re-autenticación y re-descarga en lugar de intentar descifrar).
  • Busca texto plano accidental en disco. Revisa archivos temporales, cachés HTTP, informes de fallos, eventos de analytics y cachés de imágenes que puedan contener PII o tokens. Revisa logs de depuración y volúmenes JSON.
  • Expira y rota. Access tokens cortos, refresh tokens protegidos y sesiones revocables en servidor. Define rotación de claves y qué hace la app cuando un token es rechazado (borrar, re-autenticar, reintentar una vez).
  • Comportamiento en reinstalación y cambio de dispositivo. Prueba desinstalar y reinstalar, luego abrir offline. Si las claves del Keystore desaparecen, la app debe fallar de forma segura (borrar datos cifrados, mostrar login, evitar lecturas parciales que corrompan el estado).

Una validación rápida es la prueba de "mal día": un usuario cierra sesión, cambia su contraseña, restaura un backup en un teléfono nuevo y abre la app en un vuelo. El resultado debe ser predecible: o los datos se descifran para el usuario correcto, o se borran y se vuelven a descargar tras iniciar sesión.

Escenario de ejemplo: una app de negocio que guarda PII offline

Construye la app con tu checklist
Construye una app de negocio con backend, web y móvil nativo sin empezar desde cero.
Probar AppMaster

Imagina una app de ventas de campo usada en zonas con mala cobertura. Los comerciales inician sesión por la mañana, revisan clientes asignados offline, añaden notas de reunión y luego sincronizan. Aquí la checklist deja de ser teoría y empieza a prevenir fugas reales.

Una división práctica:

  • Access token: mantenlo de corta duración y guárdalo en EncryptedSharedPreferences.
  • Refresh token: protégelo más y exige un umbral de acceso vía Android Keystore.
  • PII de clientes (nombres, teléfonos, direcciones): almacénala en una base de datos local encriptada.
  • Notas offline y adjuntos: guárdalos en la base de datos encriptada, con cuidado extra para exportaciones y compartición.

Ahora añade dos features y el riesgo cambia.

Si añades "remember me", el refresh token se convierte en la puerta principal para volver a la cuenta. Trátalo como una contraseña. Dependiendo de tus usuarios, puedes exigir desbloqueo del dispositivo (PIN/patrón/biométrico) antes de descifrarlo.

Si añades modo offline, ya no proteges solo una sesión. Proteges una lista completa de clientes que puede tener valor por sí sola. Eso suele empujarte hacia cifrado de base de datos más reglas claras de logout: borrar PII local, mantener solo lo necesario para el siguiente login y cancelar la sincronización en background.

Prueba en dispositivos reales, no solo en emuladores. Al menos, verifica comportamiento de bloqueo/desbloqueo, reinstalación, backup/restore y separación entre usuarios o perfiles de trabajo.

Próximos pasos: convertirlo en hábito del equipo

El almacenamiento seguro solo funciona cuando es un hábito. Escribe una política corta de almacenamiento que tu equipo pueda seguir: qué va dónde (Keystore, EncryptedSharedPreferences, base de datos encriptada), qué nunca se almacena y qué debe borrarse al cerrar sesión.

Hazlo parte de la entrega diaria: definition of done, code review y checks de release.

Una lista de revisión ligera para reviewers:

  • Cada elemento almacenado está etiquetado (token, material de clave o PII).
  • La elección de almacenamiento está justificada en comentarios de código.
  • Logout y cambio de cuenta eliminan los datos correctos (y solo esos datos).
  • Errores y logs nunca imprimen secretos o PII completa.
  • Alguien es responsable de la política y la mantiene actualizada.

Si tu equipo usa AppMaster (appmaster.io) para construir apps de negocio y exporta Kotlin para el cliente Android, mantén el mismo enfoque del wrapper SecureStorage para que el código generado y el personalizado sigan una política consistente.

Empieza con un pequeño proof-of-concept

Construye un pequeño POC que guarde un token de autenticación y un registro PII (por ejemplo, el teléfono de un cliente necesario offline). Luego prueba instalación limpia, actualización, logout, cambios en pantalla de bloqueo y borrar datos de la app. Expande solo después de que el comportamiento de borrado sea correcto y repetible.

FAQ

¿Cuál es la forma "segura por defecto" más simple para almacenar tokens y datos de usuario en una app Kotlin de negocio?

Empieza por listar exactamente qué guardas y por qué. Pon secretos de sesión pequeños como access y refresh tokens en EncryptedSharedPreferences, conserva material criptográfico en Android Keystore y usa una base de datos encriptada para registros de negocio y PII offline cuando tengas más de un par de campos o necesites consultas.

¿Por qué las SharedPreferences en texto plano no son adecuadas para tokens o PII?

Las SharedPreferences en texto plano almacenan valores en un archivo que a menudo puede leerse desde backups del dispositivo, acceso en dispositivos rooteados o artefactos de depuración. Si el valor es un token o cualquier PII, tratarlo como una configuración normal facilita copiarlo y reutilizarlo fuera de la app.

¿Cuándo debo usar Android Keystore en lugar de cifrar un archivo yo mismo?

Usa Android Keystore para generar y mantener claves criptográficas que no deben ser extraíbles. Normalmente usas esas claves para cifrar otros datos (tokens, claves de base de datos, archivos) y, opcionalmente, exigir autenticación del usuario (biométrica o credencial del dispositivo) antes de usar la clave.

¿De qué me protege realmente un Keystore "hardware-backed"?

Significa que las operaciones con la clave pueden ocurrir en hardware protegido, de modo que el material de la clave sea más difícil de extraer incluso si un atacante puede leer archivos de la app. No asumas que está siempre disponible ni que se comporte igual en todos los dispositivos; diseña para fallos y ten un flujo de recuperación cuando las claves no estén disponibles o se invaliden.

¿EncryptedSharedPreferences es suficiente para la mayoría de las apps?

Suele ser suficiente para un pequeño conjunto de secretos tipo clave-valor que se leen con frecuencia, como access/refresh tokens, IDs de sesión y pequeños estados. No es adecuado para datos grandes, registros estructurados offline o cualquier cosa que necesites consultar y filtrar, como clientes, tickets u órdenes.

¿Cuándo necesito cifrar la base de datos en lugar de usar preferencias encriptadas?

Elige una base de datos encriptada cuando almacenes datos de negocio offline o PII a escala, necesites buscar/filtrar/ordenar o conservar historial para uso offline. Reduce el riesgo de que un dispositivo perdido exponga listas completas de clientes o notas, y permite trabajar offline con una estrategia clara de claves.

¿Debo cifrar toda la base de datos o solo campos específicos?

El cifrado de toda la base de datos protege el archivo completo en reposo y es más fácil de razonar porque no tienes que rastrear qué columnas son sensibles. El cifrado por campo puede funcionar para unas pocas columnas, pero complica la búsqueda y el ordenamiento, y es fácil filtrar datos accidentalmente a través de índices o campos derivados.

¿Cuál es una forma práctica de gestionar claves sin hardcodearlas?

Genera una clave aleatoria para la base de datos y guárdala solo en forma "wrapped" (cifrada) usando una clave respaldada por Android Keystore. Nunca hardcodees claves ni las incluyas en la app, y decide qué ocurre al cerrar sesión o invalidar la clave (a menudo: eliminar la clave envuelta y tratar los datos locales como descartables).

¿Qué debe hacer mi app cuando las claves de Keystore se invalidan y falla el descifrado?

Las claves pueden invalidarse por cambios en la pantalla de bloqueo o biometría, eventos de seguridad del SO o escenarios de restauración/migración. Trátalo explícitamente: detecta fallos de descifrado, borra de forma segura los blobs cifrados o la base de datos local, y solicita al usuario iniciar sesión de nuevo en lugar de entrar en bucles o caer a almacenamiento en texto plano.

¿Cuáles son los errores más comunes que siguen filtrando secretos incluso si uso cifrado?

La mayoría de las fugas ocurren "fuera de la bóveda": logs, informes de fallos, eventos de analytics, impresiones de depuración, cachés HTTP, capturas de pantalla, portapapeles y rutas de backup/restore. Trata los logs como públicos, nunca registres tokens ni PII completa, desactiva rutas de exportación accidentales y asegúrate de que el logout borre datos en memoria y persistentes.

Fácil de empezar
Crea algo sorprendente

Experimente con AppMaster con plan gratuito.
Cuando esté listo, puede elegir la suscripción adecuada.

Empieza