Los 10 Riesgos Principales
Uso Incorrecto de Credenciales
Vector de ataque
Acceso a datos de autenticación mal almacenados o expuestos en el dispositivo.
Debilidad
Credenciales almacenadas sin cifrado o dentro del código fuente.
Impacto
Puede permitir que un atacante acceda a cuentas o funcionalidades restringidas.
Estrategias de mitigación
- Almacenar credenciales en lugares seguros como Keystore o Keychain.
- Evitar insertar claves en el código fuente.
- Usar autenticación basada en tokens temporales.
Ejemplos de implementación
// Código vulnerable (Android)
// ❌ Guardar usuario y contraseña sin cifrar en SharedPreferences
val prefs = getSharedPreferences("creds", MODE_PRIVATE)
prefs.edit().putString("username", "admin").apply()
prefs.edit().putString("password", "123456").apply()// Código seguro (Android)
// ✅ Usar EncryptedSharedPreferences para cifrar valores almacenados
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedPrefs = EncryptedSharedPreferences.create(
"secure_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encryptedPrefs.edit().putString("password", "123456").apply()// Código vulnerable (Android)
// ❌ Contraseña codificada directamente en la app
fun login() {
val credentials = mapOf("user" to "admin", "pass" to "123456")
sendToServer(credentials)
}// Código seguro (Android)
// ✅ Recupera la contraseña desde almacenamiento seguro
fun loginSecure() {
val password = getPasswordFromSecureStorage()
val credentials = mapOf("user" to "admin", "pass" to password)
sendToServer(credentials)
}// Código vulnerable (Android)
// ❌ Codifica credenciales directamente, sin cifrado ni token
val creds = "admin:123456"
val base64 = Base64.encodeToString(creds.toByteArray(), Base64.NO_WRAP)
request.setHeader("Authorization", "Basic $base64")// Código seguro (Android)
// ✅ Utiliza token de autenticación seguro
val token = secureTokenProvider.getToken()
request.setHeader("Authorization", "Bearer $token")Seguridad Inadecuada en la Cadena de Suministro
Vector de ataque
Inclusión de librerías o SDKs de terceros vulnerables o maliciosos.
Debilidad
Dependencias con código no verificado o con permisos excesivos.
Impacto
Puede introducir puertas traseras o facilitar ataques indirectos.
Estrategias de mitigación
- Revisar la seguridad de cada librería o SDK antes de incluirlos.
- Actualizar dependencias con regularidad.
- Usar herramientas de análisis de composición de software (SCA).
Ejemplos de implementación
// Código vulnerable (Android)
// Agregando dependencia directa desde un repositorio privado no verificado
implementation("com.untrusted.sdk:tracking-lib:1.0.0")// Código seguro (Android)
// ✅ Uso de librería de repositorio oficial y verificación de checksum
implementation("com.google.firebase:firebase-analytics:21.3.0")
// Además, usar Gradle Dependency Verification con archivo checksum// Código vulnerable (Android)
// ❌ Dependencia con código ofuscado sin auditoría previa
implementation("org.unknown:lib-obfuscated:1.1.0")// Código seguro (Android)
// ✅ Uso de librería auditada de código abierto con versión mantenida
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")// Código vulnerable (Android)
// ❌ No se usa verificación de firma en el artefacto descargado
implementation("com.malicious.analytics:core:3.2.0")// Código seguro (Android)
// ✅ Aplicación de firma PGP o checksum para verificar la integridad
dependencies {
implementation("com.segment.analytics.android:analytics:4.10.4")
}
// Verificado desde Maven CentralAutenticación/Autorización Insegura
Vector de ataque
Manipulación del flujo de autenticación o reutilización de tokens.
Debilidad
Mecanismos de control de acceso mal implementados o débiles.
Impacto
Permite a usuarios no autorizados realizar acciones restringidas.
Estrategias de mitigación
- Implementar autenticación multifactor.
- Verificar tokens del lado del servidor en cada solicitud.
- Evitar confiar únicamente en validaciones del lado cliente.
Ejemplos de implementación
// Código vulnerable (Android)
// ❌ Guardar token en preferencias sin cifrado
val prefs = getSharedPreferences("prefs", MODE_PRIVATE)
prefs.edit().putString("authToken", token).apply()// Código seguro (Android)
// ✅ Guardar token usando EncryptedSharedPreferences
val securePrefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
securePrefs.edit().putString("authToken", token).apply()// Código vulnerable (Android)
// ❌ Validación local insegura
if (username == "admin" && password == "1234") {
login()
}// Código seguro (Android)
// ✅ Validación a través del backend con token JWT y posible MFA
api.login(username, password).enqueue {
if (response.isSuccessful) {
prompt2FA()
}
}// Código vulnerable (Android)
// ❌ Acceso a actividad sin comprobar autenticación
startActivity(Intent(this, ProfileActivity::class.java))// Código seguro (Android)
// ✅ Comprobar autenticación antes de abrir actividad
if (sessionManager.isAuthenticated()) {
startActivity(Intent(this, ProfileActivity::class.java))
} else {
showLoginPrompt()
}Validación Insuficiente de Entrada/Salida
Vector de ataque
Inyección de datos maliciosos a través de formularios o parámetros.
Debilidad
Falta de sanitización o validación de datos de entrada.
Impacto
Puede dar lugar a inyecciones SQL, XSS o corrupción de datos.
Estrategias de mitigación
- Validar y sanear todos los datos del usuario.
- Utilizar listas blancas de entrada permitida.
- Aplicar encoding de salida al presentar datos.
Ejemplos de implementación
// Código vulnerable (Android)
// ❌ Uso del email sin validarlo
val email = emailInput.text.toString()
sendToServer(email)// Código seguro (Android)
// ✅ Validación con expresión regular
val email = emailInput.text.toString()
val regex = Regex("[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+")
if (regex.matches(email)) {
sendToServer(email)
} else {
showToast("Email inválido")
}// Código vulnerable (Android)
// ❌ SQL sin validación de entrada
val query = "INSERT INTO users (name) VALUES ('$userInput')"
database.execSQL(query)// Código seguro (Android)
// ✅ Uso de SQLiteStatement preparado
val stmt = database.compileStatement("INSERT INTO users (name) VALUES (?)")
stmt.bindString(1, userInput)
stmt.execute()// Código vulnerable (Android)
// ❌ Parseo de JSON sin validación
val user = gson.fromJson(response, User::class.java)
showUser(user)// Código seguro (Android)
// ✅ Verificar campos requeridos después del parsing
val user = gson.fromJson(response, User::class.java)
if (!user.username.isNullOrEmpty() && user.id > 0) {
showUser(user)
} else {
showError()
}Comunicación Insegura
Vector de ataque
Intercepción de tráfico entre la app y el servidor a través de redes públicas.
Debilidad
Transmisión de datos sin cifrado o sin validación de certificados.
Impacto
Permite a atacantes espiar o modificar información sensible.
Estrategias de mitigación
- Usar HTTPS con certificados válidos y verificados.
- Implementar pinning de certificados.
- Evitar el uso de redes públicas para operaciones críticas.
Ejemplos de implementación
// Código vulnerable (Android)
// ❌ Permitir tráfico HTTP en AndroidManifest.xml
<application
android:usesCleartextTraffic="true"
...>
</application>
// ❌ Cliente HTTP sin validación de certificados
val client = OkHttpClient.Builder()
.hostnameVerifier { hostname, session -> true } // ❌ Acepta cualquier certificado
.build()// Código seguro (Android)
// ✅ Bloquear tráfico HTTP en AndroidManifest.xml
<application
android:usesCleartextTraffic="false"
...>
</application>
// ✅ Implementación de Certificate Pinning con OkHttp
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/HASH_DEL_CERTIFICADO=")
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()// Código vulnerable (Android)
// ❌ Envío de datos sensibles sin cifrado adicional
fun sendPaymentInfo(cardNumber: String, cvv: String, expiry: String) {
val paymentData = JSONObject().apply {
put("cardNumber", cardNumber) // ❌ Datos sensibles en texto plano
put("cvv", cvv)
put("expiry", expiry)
}
// ❌ Aunque use HTTPS, los datos van en texto plano en el cuerpo
apiService.processPayment(paymentData)
}// Código seguro (Android)
// ✅ Cifrado adicional de datos sensibles (end-to-end encryption)
fun sendPaymentInfo(cardNumber: String, cvv: String, expiry: String) {
// ✅ Obtener clave pública del servidor
val serverPublicKey = getServerPublicKey()
// ✅ Añadir timestamp y nonce para prevenir ataques de replay
val paymentData = JSONObject().apply {
put("cardNumber", cardNumber)
put("cvv", cvv)
put("expiry", expiry)
put("timestamp", System.currentTimeMillis())
put("nonce", UUID.randomUUID().toString())
}
// ✅ Cifrar datos con la clave pública del servidor
val encryptedData = encryptWithRsa(paymentData.toString(), serverPublicKey)
// ✅ Enviar solo datos cifrados
apiService.processEncryptedPayment(encryptedData)
}// Código vulnerable (Android)
// ❌ Falta de validación de respuestas del servidor
suspend fun getUserProfile(userId: String): UserProfile {
val response = apiService.getUserProfile(userId)
// ❌ No se valida la respuesta ni se manejan errores adecuadamente
val userProfile = response.body()
// ❌ Se asume que la respuesta es válida y contiene datos
return userProfile!!
}// Código seguro (Android)
// ✅ Validación adecuada de respuestas del servidor
suspend fun getUserProfile(userId: String): Result<UserProfile> {
return try {
val response = apiService.getUserProfile(userId)
// ✅ Verificar código de respuesta HTTP
if (!response.isSuccessful) {
return Result.failure(HttpException(response.code(), response.message()))
}
// ✅ Verificar que el cuerpo de la respuesta no sea nulo
val userProfile = response.body() ?: return Result.failure(
ApiException("Respuesta vacía del servidor")
)
// ✅ Verificar firma digital de la respuesta (si está disponible)
val signature = response.headers()["X-Signature"]
if (signature != null && !verifySignature(userProfile, signature)) {
return Result.failure(SecurityException("Firma inválida"))
}
// ✅ Validar estructura y contenido de la respuesta
validateUserProfile(userProfile)
Result.success(userProfile)
} catch (e: Exception) {
Result.failure(e)
}
}Controles de Privacidad Inadecuados
Vector de ataque
Acceso o compartición de datos personales sin consentimiento explícito.
Debilidad
Recolección excesiva de información o sin justificación técnica.
Impacto
Compromete la privacidad del usuario y viola regulaciones legales.
Estrategias de mitigación
- Solicitar permisos solo cuando sean necesarios.
- Aplicar el principio de minimización de datos.
- Incluir avisos de privacidad claros y comprensibles.
Ejemplos de implementación
// Código vulnerable (Android)
// ❌ Solicita permiso de ubicación sin justificación al iniciar la app
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)// Código seguro (Android)
// ✅ Solicita el permiso en el momento de uso y con aviso previo
if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
// Mostrar mensaje explicativo al usuario
}
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)// Código vulnerable (Android)
// ❌ Registro de información sensible
Log.d("DEBUG", "Access Token: $token")// Código seguro (Android)
// ✅ Evitar mostrar información sensible en logs
Log.d("DEBUG", "Token recibido correctamente")// Código vulnerable (Android)
// ❌ Guardar datos sensibles sin cifrado en SharedPreferences
val prefs = getSharedPreferences("prefs", Context.MODE_PRIVATE)
prefs.edit().putString("password", "123456").apply()// Código seguro (Android)
// ✅ Uso de EncryptedSharedPreferences
val masterKey = MasterKey.Builder(this)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val securePrefs = EncryptedSharedPreferences.create(
this, "secure_prefs", masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
securePrefs.edit().putString("password", "123456").apply()Protecciones Binarias Insuficientes
Vector de ataque
Análisis estático del binario o ingeniería inversa por parte del atacante.
Debilidad
Código fácilmente descompilable o sin protección contra modificaciones.
Impacto
Permite la creación de versiones maliciosas o piratería de la app.
Estrategias de mitigación
- Ofuscar el código antes de su compilación.
- Usar detección de debugging y hooking.
- Aplicar firmas digitales a los binarios.
Ejemplos de implementación
// Código vulnerable (Android)
// ❌ Logging en producción
Log.d("DEBUG", "Token: $token")// Código seguro (Android)
// ✅ Evitar logs en producción
if (BuildConfig.DEBUG) {
Log.d("DEBUG", "Token presente")
}// Código vulnerable (Android)
// ❌ APK no validado
fun startApp() {
println("App iniciada")
}// Código seguro (Android)
// ✅ Validación de firma del APK
val info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
val signatures = info.signingInfo.apkContentsSigners
val valid = signatures.any {
it.toCharsString().contains("MIIBIjAN...") // Parte del certificado esperado
}// Código vulnerable (Android)
// ❌ Sin detección de root
fun isSecure() = true// Código seguro (Android)
// ✅ Detección básica de root
fun isDeviceRooted(): Boolean {
val rootPaths = arrayOf("/system/app/Superuser.apk", "/system/xbin/su")
return rootPaths.any { File(it).exists() }
}Configuración de Seguridad Incorrecta
Vector de ataque
Explotación de configuraciones por defecto o mal aplicadas en el entorno de la app.
Debilidad
Permisos mal definidos, errores de despliegue o configuración por defecto.
Impacto
Puede abrir puertas a accesos no autorizados o filtraciones de información.
Estrategias de mitigación
- Revisar y limitar permisos solicitados.
- Eliminar configuraciones y archivos de prueba en producción.
- Desactivar funcionalidades de debugging en versiones finales.
Ejemplos de implementación
// Código vulnerable (Android)
/* ❌ AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_SMS" />
No se usa este permiso en la app */
fun main() {
println("App iniciada con permisos innecesarios")
}// Código seguro (Android)
/* ✅ AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
Solo se solicita lo que realmente se necesita */
fun main() {
println("App iniciada con permisos mínimos")
}// Código vulnerable (Android)
val baseUrl = "https://dev.api.miapp.com" // ❌ entorno de desarrollo
println("❌ Usando entorno de prueba")// Código seguro (Android)
val baseUrl = "https://api.miapp.com" // ✅ entorno de producción
println("✅ Entorno correcto en producción")// Código vulnerable (Android)
println("❌ Log de debugging activo en producción")// Código seguro (Android)
if (!BuildConfig.DEBUG) {
println("✅ Producción sin logs de debug")
}Almacenamiento de Datos Inseguro
Vector de ataque
Acceso físico al dispositivo o uso de malware para leer datos locales.
Debilidad
Guardar datos sensibles sin cifrado o en ubicaciones accesibles.
Impacto
Permite la extracción de información privada en caso de pérdida o robo del equipo.
Estrategias de mitigación
- Cifrar todos los datos sensibles almacenados localmente.
- Utilizar mecanismos de almacenamiento seguros del sistema operativo.
- Evitar almacenar información sensible innecesaria.
Ejemplos de implementación
// Código vulnerable (Android)
val prefs = context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
prefs.edit().putString("user_password", "usuario123").apply() // ❌ Guardado sin cifrado// Código seguro (Android)
val masterKey = MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val securePrefs = EncryptedSharedPreferences.create(
context, "secure_prefs", masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
securePrefs.edit().putString("user_password", "usuario123").apply() // ✅ Almacenamiento cifrado// Código vulnerable (Android)
val token = "jwt-token-aqui"
val prefs = context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
prefs.edit().putString("auth_token", token).apply() // ❌ Persistencia innecesaria// Código seguro (Android)
val token = "jwt-token-aqui"
// ✅ Mantener en memoria mientras dure la sesión
val sessionManager = SessionManager()
sessionManager.setToken(token)// Código vulnerable (Android)
val file = File(context.filesDir, "datos.txt")
file.writeText("nombre: Juan, DNI: 12345678") // ❌ Datos sensibles sin cifrar// Código seguro (Android)
val plainText = "nombre: Juan, DNI: 12345678"
val secretKey = SecretKeySpec(keyBytes, "AES")
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val encrypted = cipher.doFinal(plainText.toByteArray())
File(context.filesDir, "datos.enc").writeBytes(encrypted) // ✅ Cifrado antes de guardarCriptografía Insuficiente
Vector de ataque
Uso de algoritmos débiles o implementación incorrecta de funciones criptográficas.
Debilidad
Uso de claves mal generadas, algoritmos obsoletos o bibliotecas caseras.
Impacto
Permite a un atacante descifrar información o falsificar datos.
Estrategias de mitigación
- Utilizar algoritmos criptográficos aprobados por estándares actuales.
- Nunca implementar criptografía propia.
- Actualizar regularmente las bibliotecas criptográficas utilizadas.
Ejemplos de implementación
// Código vulnerable (Android)
import java.security.MessageDigest
// ❌ Uso de MD5
val input = "password"
val md = MessageDigest.getInstance("MD5")
val digest = md.digest(input.toByteArray())
println("❌ Hash MD5: ${digest.joinToString("") { "%02x".format(it) }}")// Código seguro (Android)
import java.security.MessageDigest
// ✅ Uso de SHA-256
val input = "password"
val md = MessageDigest.getInstance("SHA-256")
val digest = md.digest(input.toByteArray())
println("✅ Hash SHA256: ${digest.joinToString("") { "%02x".format(it) }}")// Código vulnerable (Android)
val secretKey = "HARDCODED_SECRET" // ❌ Clave embebida
println("❌ Usando clave embebida: $secretKey")// Código seguro (Android)
val secretKey = BuildConfig.API_SECRET // ✅ Clave configurada por buildConfigField
println("✅ Clave segura desde configuración")// Código vulnerable (Android)
val sharedPref = context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
sharedPref.edit().putString("auth_token", "token123").apply() // ❌ Guardado inseguro
println("❌ Token en SharedPreferences")// Código seguro (Android)
val masterKey = MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val encPrefs = EncryptedSharedPreferences.create(
context, "secure_prefs", masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encPrefs.edit().putString("auth_token", "token123").apply()
println("✅ Token cifrado en almacenamiento seguro")