当应用程序被卸载时删除钥匙串项目

268

我正在使用idandersen的scifihifi-iphone代码来操作钥匙串,并保存密码。

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

在设备上删除应用程序后,密码仍然存在于钥匙串中。

我希望在用户从设备中删除应用程序时从钥匙串中删除密码。我该怎么做?


13
由于在删除应用程序时您的代码不会运行,因此您无法实现这一点。 - Jonathan Grynspan
2
我认为你只能在应用程序内部删除钥匙串项目,而不能在卸载之前删除。您可以查看SFHFKeychainUtils的deleteItem方法,以从钥匙串中删除用户名或密码。 - matteodv
9个回答

436

你可以利用NSUserDefaults被应用卸载时清除这一事实。例如:

- (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
}

如果尚未设置,此代码将检查并在应用程序的第一次运行时在NSUserDefaults中设置“FirstRun”键/值。在您应该放置从密钥链中删除值的代码处有一条注释。可以调用Synchronize来确保“FirstRun”键/值立即持久化,以防用户在系统持久化之前手动杀死应用程序。


2
我同意Amro的观点,即在应用程序第一次运行时可以删除/清除您的钥匙串。这将清除在上次卸载应用程序之前设置的所有内容。我为我的一个存储Facebook / Twitter凭据的应用程序执行了此操作,并且已经非常好地工作,因为只有您的应用程序可以访问设置的任何钥匙串。 - XCool
3
当用户手动退出应用程序时,NSUserDefaults 不会被清除。在这种情况下,仅丢失尚未与磁盘同步的系统(定期)或您设置但尚未通过调用 "synchronize" 同步的值。在设置第一个运行键后调用 synchronize 是一个好主意。是的,在设备重置(而不是从备份中恢复)时,NSUserDefaults 会被清除,这种情况下是可以接受的。 - Amro
5
你的观点是错误的,你可能在做某些导致用户默认设置被清除的事情。NSUserDefaults 的整个目的是保存偏好设置,并使这些偏好设置持续存在于多次应用程序启动中。再次说明,重置设备或删除应用程序将删除用户默认设置。看看有多少人赞成了这个答案并检查你的代码。然后去阅读文档。实际上,把相关代码发给我,我会展示给你你所做的错误之处。自iOS 2.0以来一直是这样的。可以进行反对票,但我建议先编写一个隔离、简单的测试案例。 - Amro
哦……你说得对。抱歉,在800个文件中找到测试代码很难。我会更新我的答案。 - stephen
10
针对此事,我不太自信使用NSUserDefault。为什么?请看这个线程:https://dev59.com/-2Ij5IYBdhLWcg3wb0nX。 如果你从后台启动应用程序,则有时候您在NSUserDefault中的自定义键可能未被设置。应用此答案将导致删除您的Keychain自定义密钥,尽管您并不希望这样做! - Aurelien Porte
显示剩余4条评论

53

对于寻找Swift 3.0版本@amro答案的用户:

let userDefaults = UserDefaults.standard

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

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

*请注意,synchronize()函数已被弃用


2
if !userDefaults.bool(forKey: "hasRunBefore") { 这样写更简洁。 - nefarianblack
1
应该移除同步调用。 - Pochi

34

当应用程序从设备中删除时,没有触发器执行代码。访问钥匙串取决于用于签署应用程序的配置文件。因此,其他应用程序将无法访问钥匙串中的此信息。

如果用户从设备中删除应用程序,它无法帮助您删除钥匙串中的密码,但应使您感到安心,因为密码是不可访问的(仅通过重新安装原始应用程序才能访问)。


2
如果我们更改应用程序的配置文件,它是否能够访问之前存储在钥匙串中的值? - Moaz Saeed
1
根据我的经验,即使配置文件更改,应用程序仍可以访问钥匙串中先前存储的值。@MoazSaeed - ricardopereira

30

对于那些寻求@amro答案的Swift版本的人:

    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
    }

旧版本的应用程序怎么样?如果您管理已登录会话,则所有这些应用程序的值可能不存在,因为所有用户都可能已注销。 - mulp
我不明白你的意思。 - rsc

10

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();
    }

...并从钥匙串中清除记录(在上面的TODO评论)

        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);
        }

2
通过使用 Xamarin.Essentials 中的 if (VersionTracking.IsFirstLaunchEver) {// remove keychain items},您无需编写 userDefaults 的代码。Xamarin.Essentials 会为您封装它 - Christopher Stephan

8

@amro的答案转换为Swift 4.0:

if UserDefaults.standard.object(forKey: "FirstInstall") == nil {
    UserDefaults.standard.set(false, forKey: "FirstInstall")
    UserDefaults.standard.synchronize()
}

甚至可以使用 if !UserDefaults.standard.bool(forKey: "FirstInstall"),如果键不存在,默认为 false。不需要 .synchronize()。 - CharlesA

8
当用户卸载应用程序时,文件将从应用程序的文档目录中删除。因此,在application:didFinishLaunchingWithOptions:方法中,您只需要首先检查文件是否存在即可。之后,无条件地创建文件(即使它只是一个虚拟文件)。
如果在检查时文件不存在,则说明这是最新安装后的第一次运行。如果您需要在应用程序后面知道这个信息,请将布尔结果保存到您的应用程序委托成员中。

2

7
我猜直到测试版5,iOS 10.3的公开版没有包含这个变更。 - Jakub Truhlář

0
只需添加一个应用程序设置包并实现一个切换按钮,以在应用程序重新启动时重置钥匙串或根据通过设置选择的值执行某些操作(可通过userDefaults获得)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接