Seguridad de aplicaciones Swift: top 10 vulnerabilidades en la práctica
El desarrollo de aplicaciones móviles para iOS y macOS requiere especial atención a la seguridad. Swift es un lenguaje potente y moderno, pero ni siquiera él protege al desarrollador de errores típicos que pueden provocar fugas de datos, hackeos o pérdidas financieras. En este artículo analizaremos las 10 principales vulnerabilidades de las aplicaciones Swift con ejemplos de código reales y recomendaciones prácticas para solucionarlas.
1. Inyecciones SQL a través de Core Data y SQLite
Muchos desarrolladores utilizan consultas SQL sin procesar junto con SQLite. Si los datos del usuario no se escapan, un atacante puede ejecutar una consulta arbitraria en la base de datos.
// ❌ Código vulnerablelet query = "SELECT * FROM users WHERE name = '\\(userInput)'"
// ✅ Código seguro (uso de consultas parametrizadas)let query = "SELECT * FROM users WHERE name = ?"var statement: OpaquePointer?sqlite3_prepare_v2(database, query, -1, &statement, nil)sqlite3_bind_text(statement, 1, (userInput as NSString).utf8String, -1, nil)Utilice consultas parametrizadas o Core Data con predicados: esto elimina las inyecciones.
2. Almacenamiento inseguro de datos en Keychain
Keychain es un lugar seguro, pero muchos desarrolladores configuran incorrectamente los atributos de acceso. Por ejemplo, si no se especifica kSecAttrAccessible, los datos pueden estar disponibles después de una copia de seguridad.
// ❌ Código vulnerable (accesible después de la copia de seguridad)let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "token", kSecValueData as String: tokenData]
// ✅ Código seguro (solo cuando el dispositivo está desbloqueado)let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "token", kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecValueData as String: tokenData]3. Falta de verificación SSL/TLS (Man-in-the-Middle)
Durante el desarrollo, a menudo se desactiva la verificación de certificados para pruebas, pero se olvida restaurar la configuración en la versión de lanzamiento. Esto permite a un atacante interceptar el tráfico.
// ❌ Código vulnerable (peligroso para producción)let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)// En el delegado:func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))}
// ✅ Código seguro: use la verificación estándarlet session = URLSession.shared// O use ATS (App Transport Security) en Info.plistActive App Transport Security (ATS) y no lo desactive en la compilación de lanzamiento.
4. Fuga de datos a través de registros y Crashlytics
Los desarrolladores a menudo registran datos sensibles (tokens, contraseñas) para depuración. Si el registro llega a un servicio de terceros (por ejemplo, Firebase Crashlytics), los datos se filtran.
// ❌ Código vulnerableprint("User token: \\(userToken)")
// ✅ Código seguro#if DEBUG print("User token: [REDACTED]")#endif// O use os_log con nivel de privacidados_log("User token: %{private}@", log: .default, type: .debug, userToken)Nunca registre datos sensibles en producción. Use enmascaramiento o compilación condicional.
5. Almacenamiento inseguro de secretos en el código
Almacenar claves API, tokens y contraseñas directamente en el código fuente es un error grave. Incluso si el código está ofuscado, las cadenas se pueden extraer fácilmente del archivo binario.
// ❌ Código vulnerablelet apiKey = "sk-1234567890abcdef"
// ✅ Código seguro: use autenticación del lado del servidor// O cifre las claves con CryptoKitimport CryptoKitlet key = SymmetricKey(data: "server_