iOS KeyChain no recupera valores del fondo

85

Actualmente estoy almacenando el nombre de usuario (correo electrónico) y un hash salado del correo electrónico y la contraseña en el KeyChain de iOS. Estoy usando la versión ARC'ified que se encuentra aquí .

KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
[wrapper setObject:APP_NAME forKey:(__bridge id)kSecAttrService];
[wrapper setObject:email forKey:(__bridge id)kSecAttrAccount];
[wrapper setObject:token forKey:(__bridge id)kSecValueData];

Todo esto funciona bien cuando necesito sacar el token para mis llamadas de red mientras la aplicación está activa. Funciona para iniciar sesión desde un inicio limpio, así como para todas las llamadas de red. El problema comienza cuando la aplicación está en segundo plano.

Tenga en cuenta que esto solo sucede esporádicamente y todavía tengo que precisarlo para una versión o dispositivo iOS específico.

El usuario dispara una ubicación (monitoreo de región) y quiero actualizar el servidor con su estado. Intento sacar el token del llavero, de la misma manera que lo hago con todas las demás llamadas de red, y actualizo el estado. Pero para algunos usuarios, el valor es nulo. Sin él, no puedo actualizar las cosas de la red. ¿Por qué funcionaría esto para la mayoría, pero no para un pequeño porcentaje?

KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyCustomIdentifier" accessGroup:nil];
NSString *token = [wrapper objectForKey:(__bridge id)kSecValueData];

Volví a la versión no ARC del keychainwrapper, pero sigo obteniendo los mismos resultados. Agradecería cualquier comentario sobre esto. Es solo una pequeña parte de mis usuarios, pero es un problema que me gustaría solucionar y no preocuparme. Gracias por adelantado.

Además, todo mi trabajo en segundo plano está configurado en backgroundTask para evitar que se agote el tiempo de espera. No tengo ningún problema con el trabajo que rodea al llavero, pero no dejo que las cosas sigan adelante hasta que mi token esté lleno.

EDITAR He descubierto mi problema con el llavero que no recupera valores del fondo. Publicaré la respuesta a continuación y la aceptaré, ya que creo que esta pregunta puede resultar valiosa para otros más adelante.

Bill Burgess
fuente

Respuestas:

110

Mi pregunta estuvo cerca de la marca por la razón, pero no del todo. Después de leer blog tras blog, tutorial tras tutorial, finalmente encontré uno que daba una pista de lo que podría estar sucediendo.

Pantallas de inicio bloqueadas. Los tutoriales de llaveros siempre dejaban en blanco la configuración de accesibilidad para el llavero, por lo que se establecería de forma predeterminada en el nivel de acceso más bajo / más seguro de Apple. Sin embargo, este nivel no permite el acceso al llavero si el usuario tiene una contraseña en la pantalla de bloqueo. ¡Bingo! Esto explica el comportamiento esporádico y por qué esto solo le ocurre a un pequeño porcentaje de usuarios.

Una línea de código resuelve todo el lío.

[wrapper setObject:(__bridge id)kSecAttrAccessibleAlways forKey:(__bridge id)kSecAttrAccessible];

Agregue esta línea donde estoy configurando los valores de nombre de usuario y contraseña. Funciona de maravilla. Espero que esto ayude a alguien. Me confundió durante bastante tiempo hasta que pude juntar las piezas.

Bill Burgess
fuente
1
¡Gracias! Esto fue muy útil.
Rich Waters
3
Literalmente hemos estado lidiando con esto durante semanas. ¡Eres un salvavidas!
OC Rickard
15
Evítelo …AccessibleAlwayssi es posible, o almacene un token que solo brinde privilegios limitados (por ejemplo, un token que le permita leer nuevos elementos de noticias, pero no publicar). Al hacerlo, renuncia explícitamente a un nivel de cifrado. Si su aplicación puede esperar hasta el primer desbloqueo, quizás sea mejor usar …AfterFirstUnlocky dirigir a sus usuarios a desbloquear sus dispositivos primero.
millenomi
14
Esta es una muy mala idea porque significa que estos datos de credenciales ya no están protegidos. Si bien es un poco más de trabajo, es importante crear una credencial derivada de la que solo se puede usar para el acceso limitado que espera que se requiera en segundo plano y nada más. Esa credencial limitada puede caducar después de un período de tiempo, y se crea una nueva cada vez que se abre la aplicación, invalidando las antiguas. Esto mantiene al usuario seguro en caso de que la credencial derivada se vea comprometida. Consulte la sesión 204 de la WWDC 2013 para conocer este tema.
Joey Hagedorn
7
haciendo eco de @JoeyHagedorn aquí: escuche la sesión 204 de la WWDC 2013 "Novedades de la multitarea" en la marca 44:24 y la sesión 709 de la WWDC 2013 "Protección de secretos con el llavero" en la marca 25:30. Puede ver el contenido de texto de estas charlas en asciiwwdc.com
Shazron
63

Utilizar en kSecAttrAccessibleAfterFirstUnlocklugar de kSecAttrAccessibleAlways.


De la documentación de Apple :

kSecAttrAccessibleAfterFirstUnlock
No se puede acceder a los datos del elemento del llavero después de un reinicio hasta que el usuario haya desbloqueado el dispositivo una vez.

Después del primer desbloqueo, los datos permanecen accesibles hasta el próximo reinicio. Se recomienda para elementos a los que deben acceder las aplicaciones en segundo plano. Los elementos con este atributo migran a un nuevo dispositivo cuando se utilizan copias de seguridad cifradas.

guau
fuente
4
Esta respuesta debería ser un comentario ...
Frizlab
Esta respuesta parece perfecta porque kSecAttrAccessibleAlwaysya está en desuso
Sazzad Hissain Khan
1

En mi caso, watchOS2 accede a los datos del llavero en el lado de iOS.

Al principio, se utiliza kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Puedo leer los datos sin importar si el iPhone está bloqueado o no. Es muy confuso para mí que recibiré un error cuando el reloj intente acceder al llavero:: SecTrustEvaluate [hoja IssuerCommonName SubjectCommonName]

Y en algún caso se convertirá en:: SecOSStatusWith error: [- 25308] Error Domain = NSOSStatusErrorDomain Code = -25308 "ks_crypt: e00002e2 falló en el elemento 'oe' (clase 6, bolsa: 0) Se intentó acceder al elemento mientras el llavero está bloqueado. " UserInfo = {NSDescription = ks_crypt: e00002e2 no pudo 'oe' elemento (clase 6, bolsa: 0) Se intentó acceder al elemento mientras el llavero estaba bloqueado.}

Actualizaré mi respuesta si obtengo más información.

skingtree
fuente
0

Esto podría suceder debido a la política de protección de datos de Apples, que en algún nivel es oscura desde la perspectiva de los desarrolladores. La solución es cuando se inicia la aplicación, verifique si el llavero es accesible o no, si no está accesible, puede eliminar su aplicación (con la ventana emergente adecuada) según los tipos de aplicación.

+(BOOL) isKeychainAccessible
{
    NSString *keychainTestKey = @"keychainTestKey";
    NSString *keychainTestValue = @"keychainTestValue";
    [self createKeychainValue:keychainTestValue forIdentifier:keychainTestKey];
    NSString *loadedValue = [self keychainStringFromMatchingIdentifier:keychainTestKey];
    [self deleteItemFromKeychainWithIdentifier:keychainTestKey];
    return ([keychainTestValue isEqualToString: loadedValue]);
}
Sazzad Hissain Khan
fuente