iOS 키 체인이 백그라운드에서 값을 검색하지 않음


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 버전 또는 기기에 아직 고정하지 않았습니다.

사용자가 위치를 이동하고 (지역 모니터링) 서버에 상태를 업데이트하고 싶습니다. 다른 모든 네트워크 호출에 대해 수행하는 것과 동일한 방식으로 키 체인에서 토큰을 꺼내고 상태를 업데이트하려고합니다. 그러나 일부 사용자의 경우 값이 nil입니다. 그것 없이는 네트워크 항목을 업데이트 할 수 없습니다. 왜 이것이 대부분의 경우 효과가 있지만 적은 비율에는 효과가 없습니까?

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

ARC 버전의 keychainwrapper로 돌아 갔지만 여전히 동일한 결과를 얻습니다. 이에 대한 의견을 보내 주시면 감사하겠습니다. 내 사용자의 작은 부분에 불과하지만 내가 고치고 싶은 문제이며 걱정하지 마십시오. 미리 감사드립니다.

또한 모든 백그라운드 작업은 시간 초과를 방지하기 위해 backgroundTask에 설정됩니다. 키 체인을 둘러싼 작업에 문제가 없지만 토큰이 채워질 때까지 일을 진행하지 않습니다.

편집 나는 배경에서 값을 검색하지 않는 키 체인 문제를 알아 냈습니다. 아래 답변을 게시하고 나중에이 질문이 다른 사람들에게 가치가있을 수 있다고 생각하므로 수락하겠습니다.

답변:


110

내 질문은 그 이유 때문에 표시에 가깝지만 정답은 아닙니다. 블로그, 튜토리얼, 튜토리얼을 읽은 후 마침내 무슨 일이 일어날 지에 대한 힌트를주는 것을 발견했습니다.

잠긴 홈 화면. 키 체인 튜토리얼은 항상 키 체인에 대한 접근성 설정을 비워 두었으므로 기본적으로 Apple의 가장 낮은 / 가장 안전한 액세스 수준으로 설정됩니다. 그러나이 수준에서는 사용자가 잠금 화면에 암호가있는 경우 키 체인 접근을 허용하지 않습니다. 빙고! 이것은 산발적 인 행동과 이것이 소수의 사용자에게만 발생하는 이유를 설명합니다.

한 줄의 코드로 전체 문제를 해결합니다.

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

사용자 이름과 암호 값을 설정하는 곳에이 줄을 추가합니다. 매력처럼 작동합니다. 이것이 누군가를 도울 수 있기를 바랍니다. 조각들을 모을 수있을 때까지 꽤나 당혹 스러웠습니다.


1
감사! 이것은 매우 도움이되었습니다.
Rich Waters

3
우리는 문자 그대로 몇 주 동안 이것을 다루었습니다. 당신은 생명의 은인입니다!
OC Rickard 2013 년

15
…AccessibleAlways가능한 한 피 하거나 제한된 권한 만 제공하는 토큰 (예 : 새 피드 항목을 읽을 수 있지만 게시 할 수없는 토큰)을 저장하십시오. 그렇게함으로써 암호화 수준을 명시 적으로 포기하는 것입니다. 앱이 첫 번째 잠금 해제까지 기다릴 수 있다면 …AfterFirstUnlock사용자가 먼저 기기를 잠금 해제 하도록 사용 하고 안내 하는 것이 가장 좋습니다 .
millenomi

14
이 자격 증명 데이터가 더 이상 보호되지 않기 때문에 이것은 정말 나쁜 생각입니다. 작업이 조금 더 많지만 백그라운드에서 필요로하는 제한된 액세스에 사용할 수있는 것보다 더 이상 파생되지 않는 자격 증명 을 만드는 것이 중요합니다 . 제한된 자격 증명은 일정 기간 후에 만료 될 수 있으며 앱이 열릴 때마다 새 자격 증명이 생성되어 이전 자격 증명을 무효화합니다. 이렇게하면 파생 자격 증명이 손상된 경우 사용자를 안전하게 보호 할 수 있습니다. 이에 대해 들어 보려면 WWDC 2013 세션 204를 참조하십시오.
Joey Hagedorn

7
@JoeyHagedorn을 여기에서 에코-44:24 마크에서 WWDC 2013 세션 204 "멀티 태스킹의 새로운 기능"및 25:30 마크에서 WWDC 2013 세션 709 "키 체인으로 비밀 보호"를 들어보십시오. 이 강연의 텍스트 내용은 asciiwwdc.com
Shazron

64

사용 kSecAttrAccessibleAfterFirstUnlock대신에 kSecAttrAccessibleAlways.


에서 애플의 설명서 :

kSecAttrAccessibleAfterFirstUnlock
키 체인 항목의 데이터는 사용자가 기기를 한 번 잠금 해제 할 때까지 다시 시작한 후 액세스 할 수 없습니다.

첫 번째 잠금 해제 후에는 다음에 다시 시작할 때까지 데이터에 액세스 할 수 있습니다. 백그라운드 애플리케이션에서 액세스해야하는 항목에 권장됩니다. 이 속성이있는 항목은 암호화 된 백업을 사용할 때 새 장치로 마이그레이션됩니다.


4
이 대답은 ... 코멘트이어야한다
Frizlab

때문에 대답은 완벽한 것 같다 kSecAttrAccessibleAlways이미 사용되지 않습니다
Sazzad Hissain 칸

1

제 경우에는 watchOS2가 iOS 측의 키 체인 데이터에 액세스합니다.

처음에는 kSecAttrAccessibleWhenUnlockedThisDeviceOnly가 사용됩니다. iPhone이 잠겨 있든 없든 데이터를 읽을 수 있습니다. 시계가 키 체인에 액세스하려고 할 때 오류가 발생한다는 점이 매우 혼란 스럽습니다. : SecTrustEvaluate [leaf IssuerCommonName SubjectCommonName]

그리고 어떤 경우에는 다음과 같이됩니다. : SecOSStatusWith error : [-25308] Error Domain = NSOSStatusErrorDomain Code = -25308 "ks_crypt : e00002e2가 'oe'항목에 실패했습니다 (class 6, bag : 0) 키 체인이 잠겨있는 동안 항목에 대한 액세스를 시도했습니다. " UserInfo = {NSDescription = ks_crypt : e00002e2가 'oe'항목에 실패했습니다 (클래스 6, bag : 0) 키 체인이 잠겨있는 동안 시도한 항목에 대한 액세스.}

더 많은 정보를 얻으면 답변을 업데이트하겠습니다.


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]);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.