iOS KeyChain не получает значения из фона

87

В настоящее время я храню имя пользователя (адрес электронной почты) и соленый хеш адреса электронной почты и пароля в iOS KeyChain. Я использую версию ARC, найденную здесь .

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

Все это отлично работает, когда мне нужно вытащить токен для моих сетевых вызовов, пока приложение активно. Он работает для входа в систему из чистого запуска, а также для всех сетевых вызовов во всем. Проблема начинается, когда приложение работает в фоновом режиме.

Имейте в виду, что это происходит спорадически, и мне еще предстоит привязать это к конкретной версии iOS или устройству.

Пользователь посещает место (мониторинг региона), и я хочу обновить сервер с его статусом. Я пытаюсь вытащить токен из связки ключей так же, как и для любого другого сетевого вызова, и обновить статус. Но для некоторых пользователей значение равно нулю. Без него я не могу обновить сетевую информацию. Почему это сработает для большинства, но не для небольшого процента?

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

Я вернулся к версии keychainwrapper без ARC, но все равно получаю те же результаты. Буду признателен за любые отзывы по этому поводу. Это лишь небольшая часть моих пользователей, но это проблема, которую я хотел бы исправить и о которой не беспокоиться. Заранее спасибо.

Кроме того, вся моя фоновая работа настроена в backgroundTask, чтобы предотвратить истечение времени ожидания. У меня нет проблем с работой, связанной с связкой ключей, но я не позволяю этому идти вперед, пока мой токен не будет заполнен.

ИЗМЕНИТЬ Я выяснил свою проблему с цепочкой ключей, не извлекающей значения из фона. Я отправлю ответ ниже и приму его, так как считаю, что этот вопрос может стать ценным для других позже.

Билл Берджесс
источник

Ответы:

110

Мой вопрос был близок к истине по причине, но не совсем. Прочитав блог за блогом, учебник за учебником, я наконец нашел тот, который дает намек на то, что может происходить.

Заблокированные домашние экраны. В руководствах по связке ключей параметры специальных возможностей для связки ключей всегда оставались пустыми, поэтому по умолчанию использовался самый низкий / самый безопасный уровень доступа Apple. Однако этот уровень не разрешает доступ к связке ключей, если у пользователя есть пароль на экране блокировки. Бинго! Это объясняет спорадическое поведение и то, почему это происходит только с небольшим процентом пользователей.

Одна строчка кода решает весь беспорядок.

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

Добавьте эту строку, где я устанавливаю значения имени пользователя и пароля. Работает как шарм. Надеюсь, это поможет кому-то там. Это сбивало меня с толку на некоторое время, пока я не смог сложить части воедино.

Билл Берджесс
источник
1
Благодаря! Это было очень полезно.
Rich Waters
3
Мы буквально неделями занимаемся этим. Ты спасатель!
OC Rickard
15
Пожалуйста, избегайте, …AccessibleAlwaysесли это вообще возможно, или сохраните токен, который предоставляет только ограниченные привилегии (например, токен, который позволяет вам читать новые элементы ленты, но не публиковать). Тем самым вы явно отказываетесь от определенного уровня шифрования. Если ваше приложение может дождаться первой разблокировки, возможно, лучше всего будет использовать …AfterFirstUnlockи направить ваших пользователей сначала разблокировать свои устройства.
millenomi
14
Это действительно плохая идея, потому что это означает, что эти учетные данные больше не защищены. Хотя немного больше работы, важно создать производные учетные данные, которые можно просто использовать для ограниченного доступа, который, как вы ожидаете, потребуется в фоновом режиме, и не более того. Срок действия этих ограниченных учетных данных может истечь через некоторый период времени, и при каждом открытии приложения создается новое, аннулируя старые. Это обеспечивает безопасность пользователя в случае компрометации производных учетных данных. Обратитесь к сеансу 204 WWDC 2013, чтобы узнать об этом.
Джои Хагедорн
7
повторяя @JoeyHagedorn здесь - послушайте сеанс 204 WWDC 2013 «Что нового в многозадачности» на отметке 44:24 и сеанс 709 WWDC 2013 «Защита секретов с помощью связки ключей» на отметке 25:30. Вы можете увидеть текст этих выступлений на asciiwwdc.com
Шазрон
64

Используйте kSecAttrAccessibleAfterFirstUnlockвместо kSecAttrAccessibleAlways.


Из документации Apple :

kSecAttrAccessibleAfterFirstUnlock
Доступ к данным в элементе связки ключей будет невозможен после перезапуска, пока устройство не будет разблокировано пользователем один раз.

После первой разблокировки данные остаются доступными до следующего перезапуска. Это рекомендуется для элементов, к которым требуется доступ фоновым приложениям. Элементы с этим атрибутом переносятся на новое устройство при использовании зашифрованных резервных копий.

гав
источник
4
Этот ответ должен быть комментарием…
Frizlab 01
Этот ответ кажется идеальным, потому что kSecAttrAccessibleAlwaysон уже устарел
Sazzad Hissain Khan
1

В моем случае watchOS2 получает доступ к данным связки ключей на стороне iOS.

Вначале используется kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Я могу читать данные независимо от того, заблокирован iPhone или нет. Меня очень сбивает с толку то, что я получаю сообщение об ошибке, когда часы пытаются получить доступ к связке ключей:: SecTrustEvaluate [лист IssuerCommonName SubjectCommonName]

В некоторых случаях это будет:: SecOSStatusWith error: [- 25308] Error Domain = NSOSStatusErrorDomain Code = -25308 «ks_crypt: e00002e2 не удалось выполнить элемент« oe »(класс 6, сумка: 0) Попытка доступа к элементу, пока связка ключей заблокирована. " UserInfo = {NSDescription = ks_crypt: e00002e2 не удалось выполнить элемент «oe» (класс 6, сумка: 0) Попытка доступа к элементу при заблокированной связке ключей.}

Я обновлю свой ответ, если получу больше информации.

Skingtree
источник
0

Это могло произойти из-за политики защиты данных Apple, которая в некоторой степени неясна с точки зрения разработчиков. Обходной путь - когда запущенное приложение проверяет, доступна ли связка ключей или нет, если она недоступна, вы можете убить свое приложение (с правильным всплывающим окном) в зависимости от типа вашего приложения.

+(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]);
}
Сазад Хисейн Хан
источник