简体   繁体   English

在KeyChain中存储登录令牌的ios无法检索,很少和随机,但一致

[英]ios storing login token in KeyChain fails to retrieve, rarely and randomly, but consistently

I'm using the ios keychain ( keychainItemWrapper / SSKeychain ) to store my app's login token and maintain logged in state. 我正在使用ios钥匙串( keychainItemWrapper / SSKeychain )存储我的应用程序的登录令牌并保持登录状态。 Currently I store a simple NSDictionary in the keychain containing my token, a token expiry and a refresh token. 目前,我在包含我的令牌,令牌到期和刷新令牌的钥匙串中存储了一个简单的NSDictionary I serialize it to NSData and storing using kSecValueData . 我将它序列化为NSData并使用kSecValueData存储。 I also set the kSecAttrAccount and kSecAttrService , but don't use those for auth. 我还设置了kSecAttrAccountkSecAttrService ,但是不要将它们用于auth。

This works great, about 95% of the time. 这种方法效果很好,大约95%的时间都是如此。 The problem is that that randomly, unpredictably and sporadically, the keychain does not return data when I request it to retrieve the token. 问题是随机,不可预测和零星地,当我请求它来检索令牌时,钥匙串不会返回数据。 It is usually after a moderate time away from the app, when reopening it. 通常在距离应用程序适度的时间之后重新打开它。 It doesn't have to be from in background, or after any specific delay though. 它不一定是在后台,也不是在任何特定的延迟之后。

It fails specifically when asking for my NSData below and returns <> instead of <ABCD EFGH IJKL ....> . 当我在下面询问我的NSData并返回<>而不是<ABCD EFGH IJKL ....>时,它会失败。 I think it is nil. 我认为这是零。 Thus the code thinks the user isn't logged in and drops them immediately on my App's Signup/Login landing page, without logout error, token expiry error, etc. If I minimize the app, then reopen, it almost always gets the correct keychain info and the user is logged in again. 因此,代码认为用户未登录并立即将其丢弃在我的应用程序的注册/登录登录页面上,没有注销错误,令牌到期错误等。如果我最小化应用程序,然后重新打开,它几乎总是获得正确的钥匙串信息和用户再次登录。

This creates a confusing experience when encountered. 遇到这会产生令人困惑的体验。 It also means the user can't maintain this true 100% logged in state, with occasionally being randomly logged out. 这也意味着用户无法保持这种真正的100%登录状态,偶尔会被随机注销。 I've been unable to predict it or debug it and changing keychain libraries, as shown below, hasn't fixed it for me. 我一直无法预测或调试它,更改钥匙串库,如下所示,并没有为我修复它。 It happens for me, and several TestFlight users, and in our production app currently. 它适用于我和几个TestFlight用户,以及我们目前的生产应用程序。

Any suggestions how to maintain keychain integrity and loading 100% of time? 有关如何维护钥匙串完整性和加载100%时间的任何建议? We're about ready to implement an NSUserDefaults backup storage on the token to use in these cases, something I really don't want to do to store an auth token. 我们准备在令牌上实现NSUserDefaults备份存储,以便在这些情况下使用,我真的不想这样做来存储身份验证令牌。

Storing: 储存:

// load keychain
KeychainItemWrapper *keychainItem = [KeychainItemWrapper keyChainWrapperForKeyID:kcIdentifier];
NSString *firstLaunch = [keychainItem objectForKey: (__bridge id)(kSecAttrAccount)];
if (firstLaunch == nil){
    // initialize if needed
    [keychainItem setObject:email forKey: (__bridge id)(kSecAttrAccount)];
    [keychainItem setObject:kcIdentifier forKey: (__bridge id)kSecAttrService];
    [keychainItem setObject:(id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible];
}

// serialize "auth" NSDictionary into NSData and store
NSString *error;
NSData *dictionaryData = [NSPropertyListSerialization dataFromPropertyList:auth format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[keychainItem setObject:dictionaryData forKey:(id)kSecValueData];

Loading: 加载:

// after similar KeychainItemWrapper initialization as above
NSData *dictionaryData = [keychainItem objectForKey:(id)kSecValueData];
NSString *error;

NSDictionary *auth = [NSPropertyListSerialization propertyListFromData:dictionaryData mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];
NSString *token = auth[@"access_token"];

I have also tried using the SSKeychain library CocoaPod that is widely available, and a wrapper around the keychain logic. 我也尝试使用广泛使用的SSKeychain库CocoaPod,以及密钥链逻辑的包装器。 It is a cleaner access but fails with the same issue. 这是一个更干净的访问,但失败了同样的问题。 Here I'm just storing NSString values since there was no direct way to store NSData in the lib. 这里我只是存储NSString值,因为没有直接的方法在lib中存储NSData

// store in keychain
[SSKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock];
[SSKeychain setPassword:auth[@"access_token"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_TOKEN];
[SSKeychain setPassword:auth[@"expires_at"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_EXPIRES_AT];
[SSKeychain setPassword:auth[@"refresh_token"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_REFRESH_TOKEN];

// load from keychain
[SSKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock];
NSString *token = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_TOKEN];
NSString *expires_at = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_EXPIRES_AT];
NSString *refresh_token = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_REFRESH_TOKEN];

Keychain does have issues at the moment, and for quite a while really. 钥匙串目前确实存在问题,而且确实存在了很长一段时间。 It sounds like you're getting off lightly as usually when it breaks a force-quit of the app is required to bring it back to life. 这听起来像你正在轻松下滑,因为它通常会在应用程序中断时强制退出,以使其恢复生机。

One thing that helps is to access the keychain just once on the first request and then cache the result in memory, if it's already in memory then just return it from there. 有一点有用的是在第一个请求上只访问一次钥匙串,然后将结果缓存到内存中,如果它已经在内存中,那么只需从那里返回它。

If you can observe a specific error when this happens then trap it and retry or, as is the current case for some unfortunate apps, kill the app. 如果您在发生这种情况时可以观察到特定的错误,那么将其捕获并重试,或者就像当前一些不幸的应用程序的情况一样,杀死应用程序。 Killing the app is actually the current guidance from Apple if you raise a tech ticket to discuss the issue with them. 杀死应用程序实际上是Apple当前的指导,如果您提出技术票据与他们讨论问题。

The only other real solution is to encrypt the data and store it in a file, but then you have issues with encryption keys so this is little better than obfuscation against a keen attacker. 唯一的另一个真正的解决方案是加密数据并将其存储在一个文件中,但是你遇到加密密钥的问题,所以这比针对敏锐的攻击者的混淆要好一些。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM