iOS KeyChain nie pobiera wartości z tła

85

Obecnie przechowuję nazwę użytkownika (adres e-mail) i zasolony skrót adresu e-mail i hasła w łańcuchu kluczy iOS. Używam wersji zweryfikowanej przez ARC, znalezionej tutaj .

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

To wszystko działa dobrze, gdy muszę wyciągnąć token dla moich połączeń sieciowych, gdy aplikacja jest aktywna. Działa przy logowaniu się z czystego startu, a także przy wszystkich połączeniach sieciowych. Problem zaczyna się, gdy aplikacja działa w tle.

Pamiętaj, że zdarza się to tylko sporadycznie i jeszcze nie przypisałem tego do konkretnej wersji lub urządzenia iOS.

Użytkownik podróżuje do lokalizacji (monitorowanie regionu) i chcę zaktualizować serwer o ich status. Próbuję wyciągnąć token z pęku kluczy, tak samo jak przy każdym innym połączeniu sieciowym, i aktualizuję status. Ale dla niektórych użytkowników wartość wynosi zero. Bez tego nie mogę aktualizować sieci. Dlaczego miałoby to działać dla większości, ale nie dla małego odsetka?

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

Wróciłem do wersji keychainwrapper innej niż ARC, ale nadal otrzymuję te same wyniki. Byłbym wdzięczny za wszelkie uwagi na ten temat. To tylko niewielka część moich użytkowników, ale jest to problem, który chciałbym naprawić i którym się nie martwię. Z góry dziękuję.

Ponadto cała moja praca w tle jest skonfigurowana w tle, aby zapobiec przekroczeniu limitu czasu. Nie mam żadnych problemów z pracą związaną z pękiem kluczy, ale nie pozwalam na to, dopóki mój token nie zostanie zapełniony.

EDYCJA Zrozumiałem mój problem z tym, że pęku kluczy nie pobierają wartości z tła. Poniżej zamieszczę odpowiedź i zaakceptuję ją, ponieważ uważam, że to pytanie może później stać się wartościowe dla innych.

Bill Burgess
źródło

Odpowiedzi:

110

Moje pytanie było bliskie celu, ale nie do końca. Po przeczytaniu każdego bloga, samouczka za samouczkiem, w końcu znalazłem taki, który zdradzał, co może się dziać.

Zablokowane ekrany główne. Samouczki dotyczące pęku kluczy zawsze pozostawiały ustawienia dostępności dla pęku kluczy puste, więc domyślnie byłby najniższy / najbezpieczniejszy poziom dostępu Apple. Ten poziom nie zezwala jednak na dostęp do pęku kluczy, jeśli użytkownik ma hasło na ekranie blokady. Bingo! To wyjaśnia sporadyczne zachowanie i dlaczego zdarza się to tylko niewielkiemu odsetkowi użytkowników.

Jedna linijka kodu rozwiązuje cały bałagan.

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

Dodaj tę linię, w której ustawiam wartości nazwy użytkownika i hasła. Działa jak marzenie. Mam nadzieję, że to pomoże komuś tam. Wprawiało mnie to w zakłopotanie przez dłuższą chwilę, zanim udało mi się poskładać wszystkie elementy.

Bill Burgess
źródło
1
Dzięki! To było bardzo pomocne.
Rich Waters
3
Dosłownie zajmujemy się tym od tygodni. Ratujesz życie!
OC Rickard
15
Unikaj, …AccessibleAlwaysjeśli to w ogóle możliwe, lub przechowuj token, który zapewnia tylko ograniczone uprawnienia (np. Token, który umożliwia odczytywanie nowych elementów kanału, ale nie wysyłanie). W ten sposób wyraźnie rezygnujesz z poziomu szyfrowania. Jeśli Twoja aplikacja może zaczekać do pierwszego odblokowania, być może najlepiej byłoby użyć …AfterFirstUnlocki skierować użytkowników, aby najpierw odblokowali swoje urządzenia.
millenomi
14
To naprawdę zły pomysł, ponieważ oznacza, że ​​te dane uwierzytelniające nie są już chronione. Chociaż wymaga to trochę więcej pracy, ważne jest, aby utworzyć pochodne poświadczenie, które można po prostu wykorzystać do ograniczonego dostępu, którego oczekujesz, że będzie wymagany w tle i nic więcej. To ograniczone poświadczenie może wygasnąć po pewnym czasie, a nowe jest tworzone za każdym razem, gdy aplikacja jest otwierana, unieważniając stare. Zapewnia to użytkownikowi bezpieczeństwo w przypadku naruszenia referencji pochodnych. Więcej informacji na ten temat można znaleźć w sesji 204 WWDC 2013.
Joey Hagedorn
7
echoing @JoeyHagedorn tutaj - posłuchaj sesji 204 „What's New With Multitasking” WWDC 2013 pod znakiem 44:24 i sesji 709 WWDC 2013 „Ochrona tajemnic za pomocą pęku kluczy” w dniu 25:30. Treść tekstową tych rozmów można zobaczyć na stronie asciiwwdc.com
Shazron
63

Posługiwać się kSecAttrAccessibleAfterFirstUnlock zamiast kSecAttrAccessibleAlways.


Z dokumentacji Apple :

kSecAttrAccessibleAfterFirstUnlock
Dane w elemencie pęku kluczy nie mogą być dostępne po ponownym uruchomieniu, dopóki urządzenie nie zostanie raz odblokowane przez użytkownika.

Po pierwszym odblokowaniu dane pozostają dostępne do następnego ponownego uruchomienia. Jest to zalecane w przypadku elementów, do których muszą mieć dostęp aplikacje działające w tle. Elementy z tym atrybutem są migrowane na nowe urządzenie podczas korzystania z zaszyfrowanych kopii zapasowych.

wątek
źródło
4
Ta odpowiedź powinna być komentarzem…
Frizlab
Ta odpowiedź wydaje się idealna, ponieważ kSecAttrAccessibleAlwaysjest już przestarzała
Sazzad Hissain Khan
1

W moim przypadku watchOS2 uzyskuje dostęp do danych pęku kluczy po stronie iOS.

Na początku używany jest kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Mogę odczytać dane niezależnie od tego, czy iPhone jest zablokowany, czy nie. Jest dla mnie bardzo mylące, że otrzymuję błąd, gdy zegarek próbuje uzyskać dostęp do pęku kluczy:: SecTrustEvaluate [liść IssuerCommonName SubjectCommonName]

W pewnym przypadku stanie się to:: SecOSStatusWith error: [- 25308] Error Domain = NSOSStatusErrorDomain Code = -25308 "ks_crypt: e00002e2 nie powiodło się" oe "item (class 6, bag: 0) Próbowano uzyskać dostęp do elementu, gdy pęku kluczy jest zablokowany. " UserInfo = {NSDescription = ks_crypt: e00002e2 nie udało się uzyskać elementu „oe” (klasa 6, torba: 0) Podjęto próbę uzyskania dostępu do elementu, gdy pęku kluczy jest zablokowany.}

Zaktualizuję odpowiedź, jeśli otrzymam więcej informacji.

skingtree
źródło
0

Może się tak zdarzyć z powodu polityki ochrony danych firmy Apple, która jest na pewnym poziomie niejasna z punktu widzenia programistów. Obejście polega na tym, że po uruchomieniu aplikacji sprawdź, czy pęk kluczy jest dostępny, czy nie, jeśli nie jest dostępny, możesz zabić aplikację (z odpowiednim wyskakującym okienkiem) w zależności od typu aplikacji.

+(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
źródło