iOS KeyChain não recupera valores do fundo

85

No momento, estou armazenando o nome de usuário (e-mail) e um hash com sal do e-mail e senha no iOS KeyChain. Estou usando a versão ARC'ified encontrada aqui .

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];

Tudo isso funciona bem quando preciso puxar o token para minhas chamadas de rede enquanto o aplicativo está ativo. Ele funciona para fazer login a partir de uma inicialização limpa, bem como para todas as chamadas de rede. O problema começa quando o aplicativo está em segundo plano.

Lembre-se de que isso só acontece esporadicamente e ainda preciso definir uma versão ou dispositivo específico do iOS.

O usuário tropeça em um local (monitoramento de região) e eu quero atualizar o servidor com seu status. Tento retirar o token do chaveiro, da mesma forma que faço para todas as outras chamadas de rede, e atualizo o status. Mas, para alguns usuários, o valor é nulo. Sem ele, não consigo atualizar as coisas da rede. Por que isso funcionaria para a maioria, mas não para uma pequena porcentagem?

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

Voltei para a versão não ARC do keychainwrapper, mas ainda obtenho os mesmos resultados. Eu gostaria de receber qualquer feedback sobre isso. É apenas uma pequena parte dos meus usuários, mas é um problema que eu gostaria de corrigir e não me preocuparia. Desde já, obrigado.

Além disso, todo o meu trabalho em segundo plano é configurado em uma backgroundTask para evitar que as coisas se esgotem. Não estou tendo problemas com o trabalho em torno do chaveiro, mas não deixo as coisas avançarem até que meu token seja preenchido.

EDITAR Eu descobri meu problema com as chaves que não recuperam valores do fundo. Vou postar a resposta abaixo e aceitá-la, pois sinto que essa pergunta pode ser valiosa para outras pessoas mais tarde.

Bill Burgess
fonte

Respostas:

110

Minha pergunta estava próxima do motivo, mas não exatamente. Depois de ler blog após blog, tutorial após tutorial, finalmente encontrei um que dava uma dica do que poderia estar acontecendo.

Telas iniciais bloqueadas. Os tutoriais das chaves sempre deixavam as configurações de acessibilidade para as chaves em branco, por isso o padrão era o nível de acesso mais baixo / mais seguro da Apple. No entanto, este nível não permite o acesso às chaves se o usuário tiver uma senha na tela de bloqueio. Bingo! Isso explica o comportamento esporádico e porque isso acontece apenas com uma pequena porcentagem de usuários.

Uma linha de código resolve toda a confusão.

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

Adicione esta linha onde estou definindo os valores de nome de usuário e senha. Funciona como um encanto. Espero que isso ajude alguém lá fora. Fiquei confuso por um bom tempo, até que consegui juntar as peças.

Bill Burgess
fonte
1
Obrigado! Isso foi muito útil.
Rich Waters
3
Estamos literalmente lidando com isso há semanas. Você é um salva-vidas!
OC Rickard
15
Evite, …AccessibleAlwaysse possível, ou armazene um token que forneça apenas privilégios limitados (por exemplo, um token que permite que você leia novos itens de feed, mas não publique). Você está renunciando explicitamente a um nível de criptografia ao fazer isso. Se seu aplicativo pode esperar até o primeiro desbloqueio, talvez seja melhor usar …AfterFirstUnlocke direcionar seus usuários para desbloquear seus dispositivos primeiro.
millenomi,
14
Essa é uma ideia realmente ruim, porque significa que esses dados de credencial não estão mais protegidos. Embora dê um pouco mais de trabalho, é importante criar uma credencial derivada do que pode ser usada apenas para o acesso limitado que você espera que seja necessário em segundo plano e nada mais. Essa credencial limitada pode expirar após algum tempo e uma nova é criada a cada vez que o aplicativo é aberto, invalidando as antigas. Isso mantém o usuário seguro caso a credencial derivada seja comprometida. Consulte a sessão 204 do WWDC 2013 para saber mais sobre isso.
Joey Hagedorn,
7
ecoando @JoeyHagedorn aqui - ouça a Sessão 204 da WWDC 2013 "Novidades em multitarefa" na marca de 44:24 e a Sessão 709 da WWDC 2013 "Protegendo os segredos com as chaves" na marca de 25:30. Você pode ver o conteúdo do texto dessas palestras em asciiwwdc.com
Shazron
63

Use em kSecAttrAccessibleAfterFirstUnlockvez de kSecAttrAccessibleAlways.


Da documentação da Apple :

kSecAttrAccessibleAfterFirstUnlock
Os dados no item das chaves não podem ser acessados ​​após uma reinicialização até que o dispositivo seja desbloqueado uma vez pelo usuário.

Após o primeiro desbloqueio, os dados permanecem acessíveis até a próxima reinicialização. Isso é recomendado para itens que precisam ser acessados ​​por aplicativos em segundo plano. Os itens com este atributo migram para um novo dispositivo ao usar backups criptografados.

trama
fonte
4
Esta resposta deve ser um comentário ...
Frizlab
Esta resposta parece perfeita porque kSecAttrAccessibleAlwaysjá está obsoleta
Sazzad Hissain Khan
1

No meu caso, watchOS2 acessa os dados das chaves no lado do iOS.

No início, kSecAttrAccessibleWhenUnlockedThisDeviceOnly é usado. Posso ler os dados independentemente de o iPhone estar bloqueado ou não. É muito confuso para mim receber um erro quando o relógio tentar acessar o keychain:: SecTrustEvaluate [leaf IssuerCommonName SubjectCommonName]

E em alguns casos ele se tornará:: SecOSStatusWith erro: [- 25308] Domínio de erro = NSOSStatusErrorDomain Code = -25308 "ks_crypt: e00002e2 falhou ao 'oe' item (classe 6, bolsa: 0) Acesso ao item tentado enquanto o keychain estava bloqueado. " UserInfo = {NSDescription = ks_crypt: e00002e2 falhou ao 'oe' item (classe 6, bolsa: 0) Acesso ao item tentado enquanto o chaveiro está bloqueado.}

Vou atualizar minha resposta se receber mais informações.

skingtree
fonte
0

Isso pode acontecer devido à política de proteção de dados da Apple, que em algum nível é obscura da perspectiva dos desenvolvedores. A solução alternativa é quando o aplicativo é lançado, verifica se o keychain está acessível ou não, se não estiver acessível, você pode encerrar o aplicativo (com pop-up adequado), dependendo dos tipos de aplicativo.

+(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
fonte