Excluir itens de chaveiro quando um aplicativo é desinstalado

238

Estou usando o código scifihifi-iphone da idandersen para chaveiro e salvar a senha usando

[SFHFKeychainUtils storeUsername:@"User" andPassword:@"123"
              forServiceName:@"TestService" updateExisting:YES error:&error];

Quando eu excluo o aplicativo do dispositivo, a senha permanece no chaveiro.

Desejo remover a senha das chaves quando o usuário excluir o aplicativo do dispositivo. Como posso fazer isso?

enc
fonte
13
Como seu código não é executado quando o aplicativo está sendo excluído, você não tem como fazer isso.
Jonathan Grynspan 23/01
1
Eu acho que você pode excluir um item de chaveiro apenas de dentro do aplicativo, mas não antes de desinstalá-lo. Você pode dar uma olhada no método deleteItem de SFHFKeychainUtils para excluir um nome de usuário ou uma senha do chaveiro.
matteodv

Respostas:

406

Você pode tirar proveito do fato que NSUserDefaults é limpo pela desinstalação de um aplicativo. Por exemplo:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //Clear keychain on first run in case of reinstallation
    if (![[NSUserDefaults standardUserDefaults] objectForKey:@"FirstRun"]) {
        // Delete values from keychain here
        [[NSUserDefaults standardUserDefaults] setValue:@"1strun" forKey:@"FirstRun"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

    //...Other stuff that usually happens in didFinishLaunching
}

Isso verifica e define uma chave / valor "FirstRun" NSUserDefaultsna primeira execução do seu aplicativo, se ainda não estiver definido. Há um comentário em que você deve colocar o código para excluir valores do chaveiro. A sincronização pode ser chamada para garantir que a chave / valor "FirstRun" seja imediatamente persistida caso o usuário mate o aplicativo manualmente antes que o sistema o persista.

Amro
fonte
2
Concordo com Amro que você pode excluir / limpar seu chaveiro na primeira execução do aplicativo. Isso limpará tudo o que foi definido antes da desinstalação do aplicativo pela última vez. Fiz isso para um dos meus aplicativos que armazena credenciais do Facebook / Twitter e está funcionando muito bem, sabendo que apenas o seu aplicativo tem acesso a qualquer chaveiro que foi definido.
XJ6
Obrigado por esta dica.
iOSAppDev 30/04/2013
3
Os padrões NSUserD não são limpos quando o usuário sai do aplicativo manualmente. Somente os valores que você definiu, mas o sistema (periodicamente) ou você ainda não sincronizou com o disco (chamando synchronize) são perdidos nesse caso. É uma boa idéia chamar a sincronização depois de definir a primeira chave de execução. E sim, os NSUserDefaults são limpos quando um dispositivo é redefinido (e não restaurado do backup), e isso é bom nesse caso.
Amro
5
Você está errado e provavelmente está fazendo algo que está fazendo com que os padrões do usuário sejam limpos. O objetivo de NSUserDefaults é salvar preferências e manter essas preferências por meio de vários lançamentos de aplicativos. Novamente, redefinir o dispositivo ou excluir um aplicativo removerá os padrões do usuário. Veja quantas pessoas votaram positivamente nessa resposta e verifique seu código. Então vá ler a documentação. Caramba, envie-me o código relevante e eu mostrarei o que você está fazendo de errado. Tem sido assim desde o iOS 2.0. Não aceite votos, mas eu sugiro que você escreva um caso de teste isolado e simples primeiro.
Amro
9
Eu não estaria muito confiante em usar o NSUserDefault para isso. Por quê? Dê uma olhada nesse segmento: stackoverflow.com/questions/20269116/… . Se você iniciar o aplicativo em segundo plano, há casos em que suas chaves personalizadas no NSUserDefaults simplesmente não estão definidas. A aplicação desta resposta levaria à exclusão de suas chaves personalizadas de chaveiro, embora você realmente não queira isso!
Aurelien Porte
40

Para usuários que procuram uma versão Swift 3.0 da resposta do @ amro:

let userDefaults = UserDefaults.standard

if !userDefaults.bool(forKey: "hasRunBefore") {
     // Remove Keychain items here

     // Update the flag indicator
     userDefaults.set(true, forKey: "hasRunBefore")
}

* observe que a função synchronize () está obsoleta

bwcooley
fonte
2
if !userDefaults.bool(forKey: "hasRunBefore") {É apenas mais limpo.
Nefarianblack
1
A chamada de sincronização deve ser removida.
Pochi
30

Não há acionador para executar o código quando o aplicativo é excluído do dispositivo. O acesso ao chaveiro depende do perfil de provisionamento usado para assinar o aplicativo. Portanto, nenhum outro aplicativo seria capaz de acessar essas informações no chaveiro.

Isso não ajuda você a remover a senha no chaveiro quando o usuário exclui o aplicativo do dispositivo, mas deve dar a você um certo conforto de que a senha não está acessível (apenas a partir de uma reinstalação do aplicativo original).

Shane Fitzgibbon
fonte
Portanto, se alterarmos o perfil de provisionamento de nosso aplicativo, ele poderá acessar valores armazenados anteriormente no chaveiro.
Moaz Saeed
27

Para quem procura uma versão Swift da resposta do @ amro:

    let userDefaults = NSUserDefaults.standardUserDefaults()

    if userDefaults.boolForKey("hasRunBefore") == false {

        // remove keychain items here


        // update the flag indicator
        userDefaults.setBool(true, forKey: "hasRunBefore")
        userDefaults.synchronize() // forces the app to update the NSUserDefaults

        return
    }
rsc
fonte
9

Versão C # Xamarin

    const string FIRST_RUN = "hasRunBefore";
    var userDefaults = NSUserDefaults.StandardUserDefaults;
    if (!userDefaults.BoolForKey(FIRST_RUN))
    {
        //TODO: remove keychain items
        userDefaults.SetBool(true, FIRST_RUN);
        userDefaults.Synchronize();
    }

... e para limpar registros do chaveiro (comentário do TODO acima)

        var securityRecords = new[] { SecKind.GenericPassword,
                                    SecKind.Certificate,
                                    SecKind.Identity,
                                    SecKind.InternetPassword,
                                    SecKind.Key
                                };
        foreach (var recordKind in securityRecords)
        {
            SecRecord query = new SecRecord(recordKind);
            SecKeyChain.Remove(query);
        }
InquisitorJax
fonte
1
Usando o if (VersionTracking.IsFirstLaunchEver) {// remove keychain items}Xamarin.Essentials, você não precisa do código para o userDefaults. O Xamarin.Essentials envolve isso para você .
Christopher Stephan
7

Os arquivos serão excluídos do diretório de documentos do seu aplicativo quando o usuário desinstalar o aplicativo. Sabendo disso, tudo o que você precisa fazer é verificar se existe um arquivo como a primeira coisa que acontece application:didFinishLaunchingWithOptions:. Depois, crie incondicionalmente o arquivo (mesmo que seja apenas um arquivo fictício).

Se o arquivo não existia no momento da verificação, você sabe que esta é a primeira execução desde a última instalação. Se precisar saber mais tarde no aplicativo, salve o resultado booleano no membro delegado do aplicativo.

Stephen
fonte
7

A resposta de @ amro traduzida para o Swift 4.0:

if UserDefaults.standard.object(forKey: "FirstInstall") == nil {
    UserDefaults.standard.set(false, forKey: "FirstInstall")
    UserDefaults.standard.synchronize()
}
Muhammad Nayab
fonte
Ou mesmo if !UserDefaults.standard.bool(forKey: "FirstInstall")qual padrão é false se a chave não existir. E .synchronize () não é necessário.
CharlesA
3

Esse parece ser o comportamento padrão no iOS 10.3, com base no comportamento que as pessoas estão testemunhando na versão beta 2. Ainda não encontrou nenhuma documentação oficial sobre isso, então, por favor, comente se tiver.

Stavash
fonte
7
Foi até o beta 5, eu acho, o lançamento público do iOS 10.3 não contém essa alteração.
Jakub Truhlář