Advertisement
  1. Code
  2. iOS SDK

Asegurar los datos de iOS en reposo: El llavero

by
Read Time:11 minsLanguages:
This post is part of a series called Securing iOS Data at Rest.
Securing iOS Data at Rest: Protecting the User's Data
Securing iOS Data at Rest: Encryption

Spanish (Español) translation by Esther (you can also view the original English article)

Cualquier aplicación que guarde los datos del usuario tiene que cuidar la seguridad y la privacidad de esos datos. Como hemos visto con las recientes violaciones de datos, puede haber consecuencias muy graves por no proteger los datos almacenados de tus usuarios. En este tutorial, aprenderás algunas de las mejores prácticas para proteger los datos de tus usuarios.

En el post anterior, aprendiste a proteger archivos usando la API de protección de datos. La protección basada en archivos es una potente función para el almacenamiento seguro de datos en masa. Pero podría ser excesivo para una pequeña cantidad de información a proteger, como una clave o contraseña. Para este tipo de artículos, el llavero es la solución recomendada.

Servicios de llavero

El llavero es un gran lugar para almacenar pequeñas cantidades de información, como cadenas sensibles e identificaciones que persisten incluso cuando el usuario elimina la aplicación. Un ejemplo podría ser un token de dispositivo o de sesión que tu servidor devuelve a la aplicación al registrarse. Ya sea una cadena secreta o un token único, el llavero se refiere a todos estos elementos como contraseñas.

Hay algunas bibliotecas de terceros populares para servicios de llavero, como Strongbox (Swift) y SSKeychain (Objective-C). O bien, si quieres tener un control total sobre tu propio código, puedes utilizar directamente la API de servicios de llavero, que es una API en C.

Voy a explicar brevemente cómo funciona el llavero. Puedes pensar en el llavero como una típica base de datos en la que se ejecutan consultas sobre una tabla. Todas las funciones de la API de llavero requieren un objeto CFDictionary que contiene atributos de la consulta.

Cada entrada del llavero tiene un nombre de servicio. El nombre del servicio es un identificador: una clave para cualquier valor que quieras almacenar o recuperar en el llavero. Para permitir que un elemento del llavero se almacene solo para un usuario específico, a menudo también querrás especificar un nombre de cuenta.

Dado que cada función de llavero toma un diccionario similar con muchos de los mismos parámetros para hacer una consulta, puedes evitar el código duplicado haciendo una función de ayuda que devuelva este diccionario de consulta.

Este código configura la consulta Dictionary con los nombres de tus cuentas y servicios y le dice al llavero que vamos a almacenar una contraseña.

De manera similar a cómo puedes establecer el nivel de protección para archivos individuales (como discutimos en el post anterior), también puedes establecer los niveles de protección para tu elemento de llavero usando la clave kSecAttrAccessible.

Añade una contraseña

La función SecItemAdd() añade datos al llavero. Esta función toma un objeto Data, lo que la hace versátil para almacenar muchos tipos de objetos. Utilizando la función de consulta de contraseñas que creamos anteriormente, vamos a almacenar una cadena en el llavero. Para ello, solo tenemos que convertir el String en Data.

Borrar una contraseña

Para evitar las inserciones duplicadas, el código anterior borra primero la entrada anterior si la hay. Escribamos ahora esa función. Para ello se utiliza la función SecItemDelete().

Recuperar una contraseña

A continuación, para recuperar una entrada del llavero, utiliza la función SecItemCopyMatching(). Devolverá un AnyObject que coincida con tu consulta.

En este código, establecemos el parámetro kSecReturnData como kCFBooleanTrue. kSecReturnData significa que se devolverán los datos reales del elemento. Otra opción podría ser devolver los atributos (kSecReturnAttributes) del elemento. La clave toma un tipo CFBoolean que contiene las constantes kCFBooleanTrue o kCFBooleanFalse. Estamos estableciendo kSecMatchLimit a kSecMatchLimitOne para que solo se devuelva el primer elemento encontrado en el llavero, en lugar de un número ilimitado de resultados.

Claves públicas y privadas

El llavero es también el lugar recomendado para almacenar objetos de clave pública y privada, por ejemplo, si tu aplicación trabaja con y necesita almacenar objetos EC o RSA SecKey.

La principal diferencia es que en lugar de decirle al llavero que almacene una contraseña, podemos decirle que almacene una clave. De hecho, podemos ser específicos estableciendo los tipos de claves almacenadas, como por ejemplo si es pública o privada. Lo único que hay que hacer es adaptar la función de ayuda a la consulta para que funcione con el tipo de clave que se desea.

Las claves generalmente se identifican utilizando una etiqueta de dominio inversa como com.midominio.miclave en lugar de los nombres de servicio y cuenta (ya que las claves públicas se comparten abiertamente entre diferentes empresas o entidades). Tomaremos las cadenas de servicio y de cuenta y las convertiremos en un objeto Tag Data. Por ejemplo, el código anterior adaptado para almacenar una SecKey privada RSA tendría el siguiente aspecto:

Contraseñas de aplicaciones

Los elementos protegidos con el indicador kSecAttrAccessibleWhenUnlocked solo se desbloquean cuando el dispositivo está desbloqueado, pero depende de que el usuario tenga un código de acceso o Touch ID configurado en primer lugar.

La credencial applicationPassword permite asegurar los elementos del llavero mediante una contraseña adicional. De esta manera, si el usuario no tiene un código de acceso o Touch ID configurado, los elementos seguirán siendo seguros, y añade una capa extra de seguridad si tienen un código de acceso configurado.

Como ejemplo de escenario, después de que tu aplicación se autentique con tu servidor, tu servidor podría devolver la contraseña a través de HTTPS que se requiere para desbloquear el elemento del llavero. Esta es la forma preferida de suministrar esa contraseña adicional. No se recomienda codificar una contraseña en el binario.

Otro escenario podría ser recuperar la contraseña adicional de una contraseña proporcionada por el usuario en tu aplicación; sin embargo, esto requiere más trabajo para asegurar adecuadamente (usando PBKDF2). En el próximo tutorial veremos cómo asegurar las contraseñas proporcionadas por el usuario.

Otro uso de una contraseña de aplicación es para almacenar una clave sensible, por ejemplo, una que no querrías que quedara expuesta solo porque el usuario aún no ha configurado una clave de acceso.

applicationPassword solo está disponible en iOS 9 y superior, por lo que necesitarás una alternativa que no utilice applicationPassword si te diriges a versiones inferiores de iOS. Para utilizar el código, tendrás que añadir lo siguiente en tu cabecera de puente:

El siguiente código establece una contraseña para la consulta Dictionary.

Observa que establecemos kSecAttrAccessControl en el Dictionary. Se utiliza en lugar de kSecAttrAccessible, que se estableció previamente en nuestro método passwordQuery. Si intentas usar ambos, obtendrás un error OSStatus -50.

Autenticación de usuarios

A partir de iOS 8, puedes almacenar datos en el llavero a los que solo se puede acceder después de que el usuario se autentique correctamente en el dispositivo con Touch ID o un código de acceso. Cuando llega el momento de que el usuario se autentique, Touch ID tendrá prioridad si está configurado, de lo contrario se presenta la pantalla del código de acceso. Guardar en el llavero no requerirá que el usuario se autentique, pero recuperar los datos sí.

Puedes configurar un elemento del llavero para que requiera la autenticación del usuario proporcionando un objeto de control de acceso establecido como .userPresence. Si no se ha configurado un código de acceso, cualquier solicitud de llavero con .userPresence fallará.

Esta función es buena cuando quieres asegurarte de que tu aplicación es utilizada por la persona adecuada. Por ejemplo, sería importante que el usuario se autenticara antes de poder iniciar sesión en una aplicación bancaria. Esto protegerá a los usuarios que hayan dejado su dispositivo desbloqueado, para que no se pueda acceder a la banca.

Además, si no tienes un componente del lado del servidor para tu aplicación, puedes utilizar esta función para realizar la autenticación del lado del dispositivo en su lugar.

Para la consulta de carga, puedes proporcionar una descripción de por qué el usuario necesita autenticarse.

Al recuperar los datos con SecItemCopyMatching(), la función mostrará la IU de autenticación y esperará a que el usuario utilice Touch ID o introduzca el código de acceso. Dado que SecItemCopyMatching() se bloqueará hasta que el usuario haya terminado de autenticarse, tendrás que llamar a la función desde un hilo de fondo para permitir que el hilo principal de la interfaz de usuario siga respondiendo.

De nuevo, estamos estableciendo kSecAttrAccessControl en la consulta Dictionary. Tendrás que eliminar kSecAttrAccessible, que se estableció previamente en nuestro método passwordQuery. El uso de ambos a la vez dará lugar a un error OSStatus -50.

Conclusión

En este artículo, has tenido un recorrido por la API de servicios de llavero. Junto con la API de protección de datos que vimos en el post anterior, el uso de esta biblioteca forma parte de las mejores prácticas para asegurar los datos.

Sin embargo, si el usuario no tiene un código de acceso o Touch ID en el dispositivo, no hay cifrado para ninguno de los dos marcos. Dado que el llavero y las API de protección de datos son utilizados habitualmente por las aplicaciones de iOS, a veces son el objetivo de los atacantes, especialmente en los dispositivos con jailbreak. Si tu aplicación no trabaja con información altamente sensible, esto puede ser un riesgo aceptable. Aunque iOS actualiza constantemente la seguridad de los marcos, seguimos estando a merced de que el usuario actualice el sistema operativo, utiliza un código de acceso fuerte y no hagas jailbreak a tu dispositivo.

El llavero está pensado para los datos más pequeños, y es posible que tengas una cantidad mayor de datos que proteger que sea independiente de la autenticación del dispositivo. Aunque las actualizaciones de iOS añaden algunas funciones nuevas y estupendas, como la contraseña de la aplicación, es posible que aún tengas que soportar versiones inferiores de iOS y seguir teniendo una seguridad sólida. Por algunas de estas razones, es posible que quieras encriptar los datos tú mismo.

El último artículo de esta serie cubre el cifrado de los datos por ti mismo utilizando el cifrado AES, y aunque es un enfoque más avanzado, esto te permite tener un control total sobre cómo y cuándo se cifran tus datos.

Así que mantente atento. ¡Y mientras tanto, echa un vistazo a algunos de nuestros otros posts sobre el desarrollo de aplicaciones para iOS!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.