I am using SecItemCopyMatching to fetch a keychain item protected by Touch ID.
However, if Touch ID unlocking fails (or the user selects "Enter Passcode"), I want to present my own PIN-entry UI.
I do not want the user to be presented with the system passcode entry UI at any point.
LAContext's evaluatePolicy method provides this, but does not offer any actual keychain security, merely local authentication.
I therefore will not use LAContext to achieve this. Is this possible with SecItemCopyMatching?
On iOS 8.3 and above, the passcode fallback option is hidden initially but still appears if the first finger presented is not recognised.
For iOS 9, two new policies have been added that do not fallback to passcode. These policies are kSecAccessControlTouchIDAny and kSecAccessControlTouchIDCurrentSet
We had similar dilemma while working on one of our in-production app. We realised that we need touch ID unlock as well as custom fallback mechanism (which requires server API for unlocking) which is stronger than 4 digit unlock password.
So, Let me try to explain how we achieve it.
Similar is expectedly done by Apple for Appstore purchase and 1Password app.
Background:
Two mechanisms to integrate Touch ID:
Use Touch ID to access credentials stored in the keychain
Issue:
If a device has Touch ID as well, the preferred method is to authenticate with Touch ID and passcode is the backup mechanism
No other fallback mechanism is permitted and Apple does not allow customisation of the fallback user interface
Use Touch ID to authenticate with the app directly (called Local Authentication)
Issue:
No permission is granted to store secrets into or retrieve secrets from the Secure Enclave
Contrary to the keychain access case, Apple does not allow device passcode authentication as a backup
Every application needs to provide its own fallback to handle failed Touch ID case with custom UI
Concern:
About storing sensitive information in the keychain:
We were tempted to use this approach but were taken aback by realising the only fallback for failing to authenticate with Touch ID is the device passcode. iOS users usually configure a four digit passcode, which is less secure than users custom passwords.
Facelifting examples:
Apple uses your iCloud account password [custom fallback mechanism] as a fallback mechanism for itunes store purchase if user fails to authenticate with Touch ID.
1Password app also has similar approach.
Conclusion
In our app we authenticate with Touch ID via LocalAuthentication, we use our 'app specific PIN unlock feature' or the client's password as the fallback mechanism.
We don't store the password on the device, failure to authenticate with Touch ID requires full authentication through servers API, if device does not have a PIN configured within app.
Sample code:
[self.laContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:reason
reply:^(BOOL success, NSError *error) {
if (success)
dispatch_async(dispatch_get_main_queue(), ^{ successBlock(); });
else
dispatch_async(dispatch_get_main_queue(), ^{ fallbackBlock(error); });
self.laContext = nil;
}
];
This should probably be a comment to bllakjakk, but my reputation does not allow me to do so yet.
I am only adding this answer because the question was specifically asking about:
fetch a keychain item protected by Touch ID
The accepted answer mentions a scenario where no credentials are stored on the device.
For completeness I wanted to mention that if your app needs to authenticate to some external entity, and you therefore have to store credentials somewhere, then the Key-Chain option is the one to consider over Local Authentication.
The fear of somebody being able to authenticate via fallback for Key-Chain if they know the device pass-code (potentially weak four-digit and so on) is a moot point because nothing keeps a user from adding their own fingerprint to the device if they possess the pass-code, and then authenticating via Key-Chain or Local Authentication without having to select the fallback.
So if you are using Local Authentication over Key-Chain because you feel that the fallback mechanism is safer, you might be overlooking that minor detail and therefore be passing up on the opportunity to store credentials in the secure enclave.
We are about to implement TouchID for a financial application and are opting for Key-Chain. The intent is to educate our users about the need for strong device pass-codes at the time when they try to enable TouchID for our app.
I hope this helps you in making a decision.
You can try hiding the Enter Password button by doing the following:
1) define global function
static bool new_isFallbackButtonVisible(id self, SEL _cmd)
{
return NO;
}
2) in your application:didFinishLaunchingWithOptions: replace isFallbackButtonVisible method of LAContext class with your new implementation by calling
class_replaceMethod(NSClassFromString(#"LAContext"), NSSelectorFromString(#"isFallbackButtonVisible"), (IMP)new_isFallbackButtonVisible, "v#:B");
There is no way to disable fallback mechanism using passcode in Keychain TouchID integration. Use LocalAuthentication instead (But LocalAuthentication just provides a TouchID auth UI though not related to the Keychain).
You can hide/customize the "Enter Password" option by setting:
LAContext *context = [[LAContext alloc] init];
context.localizedFallbackTitle = #"";
and the option will disappear, or:
LAContext *context = [[LAContext alloc] init];
context.localizedFallbackTitle = #"Disable TouchID";
to customize the option text. While I know this isn't exactly what the OP was asking, it's most certainly related and could "fallback" into wanting minds.
Related
I'm working on iOS app in which I need to integrate touch id and passcode, i googled and found many tutorials for touch id, I followed this and done successfully. Every thing fine but on popup which is saying Enter Password
as shown in following pic i have few queries.
Is it possible to use apple default passcode view as shown in lock screen?
If Yes, then will apple allow to upload app onto appstore?
Looking for help.
Thanks
to hide password option try this..
var LocalAuthentication = LAContext()
LocalAuthentication.localizedFallbackTitle = "" // Add this line
tutorial which you have mentioned is using only Biometrics authentication with deviceOwnerAuthenticationWithBiometrics which Indicates that the device owner authenticated using Touch ID refer this .
If you want to authenticate with any of one (Touch id or passcode) , use deviceOwnerAuthentication instead.
replace
[context .evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics ..
with
[context .evaluatePolicy(LAPolicy.deviceOwnerAuthentication ..
We can't use Apple's default lockscreen view inside our apps, However you can use this library on github.
My doubt is after successful login using Facebook account kit for the first time, I am reopening the app for the 2nd time. When I open how can I detect and use the existing user Access Token?
I tried by fetching Accountkit.currentAccessToken in appdelegate as a condition to show login page or welcome page. But currentAccessToken returns me null.
I have also enabled "Enable Client Access Token Flow" in app settings.
Kindly help to understand if my approach is right.
self.accountKit = AKFAccountKit (responseType: AKFResponseType.accessToken)
if(self.accountKit.currentAccessToken != nil)
{
//SHOW WELCOME PAGE
}else{
//SHOW LOGIN PAGE
}
I am facing the same issue in Android (not in iOS) but I think the problem is same, since in the document says it so.
this is my code for configuring the login using Email (kotlin, use let to change the val).
val configurationBuilder = AccountKitConfiguration.AccountKitConfigurationBuilder(
LoginType.EMAIL,
AccountKitActivity.ResponseType.CODE)
notice that the response type is AccountKitActivity.ResponseType.CODE
In the document says:
If you began the login session with AccountKitActivity.ResponseType.TOKEN, a logout option is available to remove the stored AccessToken from the device.
So the problem is on the Response type that I use, need to be changed into AccountKitActivity.ResponseType.TOKEN in order to make the token stored on devices.
I have seen some issues with Xcode8 / iOS 10 Simulators and keychain save/retrieval. See https://forums.developer.apple.com/thread/60617
AccountKit stores the accessToken securely in the keychain but there is an intermittent bug on Xcode8 / iOS 10 Simulators that makes read/write from keychain fail.
Can you test if this is also happening on a Device?
Apparently the problem goes away if you add the Shared Keychain Entitlement to your App, but this is still a bug on Apple.
I need to securely store/retrieve items on iOS keychain.
From Apple KeyChainServicesReference doc (https://developer.apple.com/library/ios/documentation/security/Reference/keychainservices/Reference/reference.html) I retrieve keychain items using SecItemCopyMatching call. The latter pops up a system prompt for authenticating against local keychain that allows using fingerprint or, alternatively, a 4-digit phone passcode.
While I consider auth with fingerprint secure, it's that 4 digit PIN option that downgrades my current security. So, my question is: is there a way to query keychain with just fingerprint option (no passcode option and if fingerprint auth fails, it just fails, no fallbacks to 4 digit passcode)
I have looked into LocalAuthentication and although the latter provides the exact flow that I need, LA doesn't give me access to the keychain and thus LA is of no use to me
Thanks a lot in advance
In LAContext.h:
/// Fallback button title.
/// #discussion Allows fallback button title customization. A default title "Enter Password" is used when
/// this property is left nil. If set to empty string, the button will be hidden.
#property (nonatomic, copy) NSString *localizedFallbackTitle;
This code removes the button and user can either use their finger or cancel:
LAContext *context = [[LAContext alloc] init];
context.localizedFallbackTitle = #"";
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics ...
You're right, in iOS 8 you have no option to store an item in Keychain and make it accessible only after successful Touch ID authentication. The kSecAccessControlUserPresence access control flag added in iOS 8 makes an item accessible after either Touch ID or Passcode authentication.
But this can now be done in iOS 9. Two Touch ID-related flags, kSecAccessControlTouchIDAny and kSecAccessControlTouchIDCurrentSet, have been added.
So, all you need to do is use either of these two flags when creating access control object for Keychain item (with SecAccessControlCreateWithFlags function), and assign that object to kSecAttrAccessControl attribute when adding the item with SecItemAdd function.
There is an example from Apple that demonstrates this; see addTouchIDItemAsync method.
Also see this post for an overview of other security-related changes in iOS 9.
I haven't dived into iOS 8 keychain yet, but remember that the 4-digit passcode is set by a user. Apple recommends that when users turn on TouchID, they also turn on a more complex password, saying "Since security is only as secure as its weakest point, you can choose to increase the security of a 4-digit passcode by using a complex alphanumeric passcode." http://support.apple.com/kb/HT5949
So, the user still has the option of using something more complex than a 4-digit passcode. You just can't force them to do it. If you really want to force them, it sounds like you have to abandon the convenience of Touch ID.
I am developing a location based Q&A SDK for mobile devices.
When a question is asked about a specific location, the server side targets the most relevant user and sends the question to that user. If the user fails to answer, the question is sent to the second best user, and so on.
The problem is that my SDK might be installed on more than one application on the device, meaning that the user can get a question more than once.
Is there a way to detect whether my SDK is installed on more than one app? I thought that sending the UDID to the server might work, but iOS UDIDs differ between applications.
You can use UIPasteboard to share data between applications on the device.
The UIPasteboard class enables an app to share data within the app and with another app. To share data with any other app, you can use system-wide pasteboards; to share data with another app that has the same team ID as your app, you can use app-specific pasteboards.
In your SDK, do something like this:
#interface SDKDetector : NSObject
#end
#implementation SDKDetector
+ (void)load
{
int numberOfApps = (int)[self numberOfAppsInDeviceUsingSDK];
NSLog(#"Number of apps using sdk:%d", numberOfApps);
}
+ (NSInteger)numberOfAppsInDeviceUsingSDK
{
static NSString *pasteboardType = #"mySDKPasteboardUniqueKey";
NSData *value = [[UIPasteboard generalPasteboard] valueForPasteboardType:pasteboardType];
NSMutableArray *storedData = [[NSKeyedUnarchiver unarchiveObjectWithData:value] mutableCopy];
if (!storedData) {
storedData = [NSMutableArray new];
}
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
if (![storedData containsObject:bundleId]) {
[storedData addObject:[[NSBundle mainBundle] bundleIdentifier]];
}
value = [NSKeyedArchiver archivedDataWithRootObject:storedData];
[[UIPasteboard generalPasteboard] setData:value forPasteboardType:pasteboardType];
return [storedData count];
}
#end
If you only want to provide an SDK, it is not possible. Apple has added security steps to prevent that for user privacy. Keychain sharing will not work, because apps must share the same bundle seed ID (see here for more info).
If you want to provide an app along with your SDK, then you could do something like Facebook does, where app sends a "helo" message, Facebook asks user and finally Facebook sends "ehlo" message.
Your App -> "I would like to use the SDK; please give me token" -> SDK Controller App -> (Remember which apps have requested use) -> "OK, you can use the SDK; here is a token: #123" -> Your App
The SDK controller app can now send the server the list of apps.
I think you can group the apps on the same device by IP address as they will use the same address to connect to your server.
So the IP address will represent the device and the API key will represent the app that uses the SDK.
Can you try using
advertisingIdentifier
Not sure whether it serves your purpose. It is explained in here ASIdentifierManager class reference : Apple doc
I think its possible using keychain, you can have an unique keychain key in which you can save anything you want, and can be accessed by other apps if available. So for your SDK, lets say if there is one app, it will register some value in keychain with a unique key which is private to your SDK only if the key doesn't exist, and if it exist you get to know, since you can save any value in keychain, you can try multiple options and combinations which suits you.
You can use KeychainItemWrapper for the implementations.
Elaboration
Lets say we have an method.
[MySDK register];
Which can be used anywhere, say in AppDelegate. The register method will generate a token for the app, for the device, which we will save in the keychain using an unique key we have defined in the SDK, say in com.mysdk.key. And while saving in keychain the SDK can actually do a registration.
We consider the above method is implemented in multiple apps.
Now we have scenario.
User installs an App-A which uses the SDK, the register method will call and create a token and will save in keychain for the first time.
Now user installs another App-B which also uses the SDK, the same register method will call, but now it will check for the key com.mysdk.key in keychain, if exist it will just update the count for the token, which meant for the device.
Note
Keychain not meant to save only unique identifier, you can save other informations too.
Update
Check demo projects https://db.tt/7xpKrgMp
The wrapper I have used in the projects is same as SDK in your case which is same in both the projects.
Cheers.
Currently i have an application which has a "Remember Me" option for storing User ID.So to store this currently i am using Keychain APIs.
But i have a doubt if by chance device is stolen and somebody jailbreak the device. Can he able to get all these data from keychain?
How to prevent this ?
The most important thing when using the KeyChain is to not use kSecAttrAccessibleAlways or kSecAttrAccessibleAlwaysThisDeviceOnly because then data is not encrypted securely (see Apple's documentation). Not using these adds a layer of security to KeyChain data, but still, a strong passcode would be required by the user to protect his data. If the user has no passcode on the device, the data is unprotected. If the user has a 4-digit passcode (the standard), the data is protected very weakly and can be brute forced in minutes.
If you require protection from jailbreak (and other attacks), your best option is to not use the KeyChain, but create an encrypted sensitive data store of your own and require the user to have a secure passcode. Store the data encrypted using a key generated from that passcode.
This could inconvenience your users, so if you wish to provide a grace period between requiring passcode, think of a way to provide a session cookie to the app which is invalidated after a set period of time.
To be extra safe I'd add another layer of security on top of everything and make a simple check if the device is jailbroken. If that's the case I'd delete the current KeyChain \ sensitive data.
Something like that:
NSString *filePath = #"/Applications/Cydia.app";
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
//Device is jailbroken --> delete KeyChain
}
Or even better:
FILE *f = fopen("/bin/bash", "r");
BOOL isbash = NO;
if (f != NULL)
{
//Device is jailbroken --> delete KeyChain
isbash = YES;
}
fclose(f);
Here is the best way for checking if Device jailbroken
Code that checks
bool forked = fork();
if (forked) {
// Device is jailbroken
}
Check this link Keychain Items, where you can enumerate all keychain items.
You can also use Protection Attributes for securing info.
Apple Docs
Good Read