mm_device_security
Plugin de Flutter que consulta al sistema operativo si el dispositivo está rooteado / con jailbreak, si es emulador / simulador, o si tiene modo desarrollador. Dart no puede saber eso: el trabajo lo hacen Kotlin (Android) y Swift (iOS). Este paquete es el puente.
Pensado para apps que deben fallar cerrado (banca, tickets, RASP ligero). App Store, Play Store y TestFlight reportan las señales reales. La única excepción es Firebase Test Lab (pre-launch automático de Play).
Heurística, no un RASP comercial. Un Magisk Hide o un emulador parcheado puede colarse. Sirve como primer filtro, no como garantía absoluta.
Contenido
- Instalación
- Uso
- API
- Qué significa cada señal
- Probar en local
- Arquitectura
- Publicar el proyecto
- Limitaciones
- Licencia
Instalación
Elige una forma. No hace falta publicar en pub.flutter-io.cn para usarlo.
A. Desde GitHub (recomendado para Warena)
En el pubspec.yaml de tu app:
dependencies:
mm_device_security:
git:
url: https://github.com/Mario-Fernando24/mm_device_security.git
ref: main
flutter pub get
Para fijar una versión, usa un tag en vez de main:
ref: v0.0.1
B. Desde una carpeta local (desarrollo)
dependencies:
mm_device_security:
path: ../plugin/mm_device_security # ajusta la ruta
C. Desde pub.flutter-io.cn (cuando el paquete esté publicado)
dependencies:
mm_device_security: ^0.0.1
flutter pub add mm_device_security
Después borra de MainActivity / AppDelegate de la app el canal
verificaciones_seguridad y los detectores. Flutter registra este plugin
solo. La pantalla de bloqueo (UI de marca) se queda en la app.
Uso
import 'package:flutter/services.dart';
import 'package:mm_device_security/mm_device_security.dart';
Future<DeviceSecurityResult> verificarDispositivo() async {
try {
return await MmDeviceSecurity().getSecuritySummary();
} on MissingPluginException {
// Tests de widget / escritorio: no hay canal nativo.
return const DeviceSecurityResult();
}
}
final resultado = await verificarDispositivo();
if (resultado.isBlocked) {
// Navega a tu PantallaBloqueo.
}
Usa siempre getSecuritySummary() para decidir. Ahí el nativo ya
relaja emulador y modo desarrollador si es Firebase Test Lab. Root /
jailbreak nunca se relaja.
Métodos sueltos (señal cruda, sin relajar Test Lab):
final plugin = MmDeviceSecurity();
await plugin.isRooted();
await plugin.isJailbroken(); // mismo chequeo, nombre iOS
await plugin.isEmulator();
await plugin.isDeveloperModeEnabled();
Alerta nativa opcional (cierra el proceso):
await plugin.showBlockAlertAndExit(
title: 'Dispositivo no permitido',
message: 'Esta aplicación no puede ejecutarse en este dispositivo.',
);
API
| Método / campo | Qué es |
|---|---|
getSecuritySummary() |
Todas las señales de una vez (método del splash) |
isRooted() / isJailbroken() |
Root Android o jailbreak iOS |
isEmulator() |
AVD, BlueStacks, Simulator, etc. |
isDeveloperModeEnabled() |
ADB / opciones de desarrollador / get-task-allow |
showBlockAlertAndExit() |
Diálogo nativo no cancelable + exit |
DeviceSecurityResult.isBlocked |
rooted || emulator || developer |
DeviceSecurityResult.isSecure |
Lo contrario de isBlocked |
DeviceSecurityResult.isTestLab |
Firebase Test Lab (solo Android) |
DeviceSecurityResult.isTestFlight |
Recibo sandboxReceipt (solo iOS) |
Los alias nativos en español (resumenSeguridad, estaRooteado, …)
siguen existiendo en Kotlin/Swift por si migras a medias. El API Dart
público está en inglés.
Qué significa cada señal
getSecuritySummary()
│
├── isRooted root (Android) o jailbreak (iOS)
├── isEmulator AVD / BlueStacks / Simulator / …
├── isDeveloperMode ADB u opciones de desarrollador
├── isTestLab Firebase Test Lab (solo Android)
├── isTestFlight recibo sandboxReceipt (solo iOS)
├── isBlocked rooted || emulator || developer
└── isSecure !isBlocked
En Test Lab el nativo fuerza isEmulator e isDeveloperMode a false
para que Play Console no rechace el pre-launch. Un usuario que instala
desde Play en BlueStacks sí se bloquea.
| Dónde corre | Emulador | Modo desarrollador | ¿Bloqueado? |
|---|---|---|---|
| AVD / Simulator | SÍ | a menudo SÍ | SÍ |
| Android físico, ADB u opciones ON | NO | SÍ | SÍ |
| Android físico, opciones OFF, sin root | NO | NO | NO |
| iPhone + run desde Xcode | NO | SÍ (get-task-allow) |
SÍ |
| iPhone + TestFlight / release | NO | NO | NO (si no hay jailbreak) |
Probar en local
No hace falta publicar. El example/ ya apunta al plugin con path: ../.
cd example
flutter pub get
flutter devices
flutter run
Tests Dart (mock del canal, sin dispositivo):
flutter test
flutter analyze
Un cambio en Kotlin/Swift no entra con hot reload: hot restart o
vuelve a flutter run.
Arquitectura
App → MmDeviceSecurity().getSecuritySummary()
→ MethodChannel('mm_device_security')
→ Kotlin o Swift corre los detectores
→ Map { isRooted, isEmulator, isDeveloperMode, … }
→ DeviceSecurityResult
| Archivo | Rol |
|---|---|
lib/mm_device_security.dart |
API que importa la app |
lib/mm_device_security_method_channel.dart |
Habla con el nativo |
android/.../MmDeviceSecurityPlugin.kt |
Canal + root + emulador + ADB + Test Lab |
ios/.../MmDeviceSecurityPlugin.swift |
Canal + jailbreak + Simulator + Developer Mode |
example/ |
App mínima para ver SÍ/NO reales |
MainActivity y AppDelegate del host quedan vacíos:
GeneratedPluginRegistrant registra el plugin leyendo el pubspec.yaml.
Publicar el proyecto
El código ya está en GitHub:
https://github.com/Mario-Fernando24/mm_device_security
Hay dos destinos distintos. Elige según visibilidad.
1. GitHub (ya lo tienes — publicar una versión)
cd /Users/mariomunoz/proyectos/flutter/plugin/mm_device_security
# Comprueba que no hay secretos
git status
flutter analyze
flutter test
git add -A
git commit -m "docs: documentación de uso, instalación y publicación"
git push origin main
En GitHub (Settings → General):
- Description:
Flutter plugin: root, jailbreak, emulator and developer-mode detection. - Topics:
flutter,plugin,android,ios,security,root,jailbreak - Visibilidad: Public si quieres que otros hagan
git:/ pub.flutter-io.cn. Private si solo Warena debe ver las heurísticas. - Releases → Draft a new release
- Tag:
v0.0.1 - Title:
v0.0.1 - Notas: copia el
CHANGELOG.md
- Tag:
Fijar esa versión en la app:
mm_device_security:
git:
url: https://github.com/Mario-Fernando24/mm_device_security.git
ref: v0.0.1
2. pub.flutter-io.cn (opcional — el paquete queda público)
Cualquiera podrá leer Kotlin/Swift. Si las heurísticas deben ser privadas,
no publiques y quédate en GitHub privado + git:.
Checklist antes de subir:
# 1. Metadatos: homepage, repository, LICENSE, CHANGELOG, version
# 2. Análisis limpio
flutter analyze
flutter test
# 3. Ensayo (no sube nada)
flutter pub publish --dry-run
Cuenta y publicación:
- Entra en pub.flutter-io.cn con la cuenta de Google.
- En la terminal:
dart pub login
flutter pub publish
- Confirma
y. La URL quedará:https://pub.flutter-io.cn/packages/mm_device_security - Sube la puntuación: en pub.flutter-io.cn → Admin → conectar el repo de GitHub.
- Siguiente versión: sube
versionyCHANGELOG.md(semver:0.0.2parche,0.1.0API,1.0.0estable), commit, tag, otra vezflutter pub publish.
Si dry-run se queja de LICENSE, homepage o description, corrige y
repite. No uses --force salvo que sepas por qué.
Limitaciones
- No sustituye a un RASP comercial (Promon, Guardsquare, etc.).
ro.debuggable/test-keysson señales del sistema, no de tu APK.- Un run desde Xcode casi siempre marca modo desarrollador.
isTestFlightpuede ser true también en installs de desarrollo (recibo sandbox).
Licencia
BSD 3-Clause. Ver LICENSE.