

How we fixed the iOS7 forced logout bug that's been affecting so many apps - jansen
https://blog.loom.com/how-we-fixed-the-ios7-forced-logout-bug-thats-been-plaguing-so-many-apps/

======
pat2man
This is not the proper solution. Instead of making your data accessible when
the device is locked you should listen for
UIApplicationProtectedDataWillBecomeUnavailable and
UIApplicationProtectedDataDidBecomeAvailable. Once your keychain item becomes
available again you can log the user back in. The keychain will always be
available in the foreground.

~~~
lnanek2
Sounds like a much better solution than many of the other proposals here,
which would end up with the user having to enter their login credentials more
often than they'd like.

------
mpetrov
At Couple, we found a much simpler solution to debug this exact same bug two
days ago, and there was no driving needed:

1\. Add lock code to your phone

2\. Open the app without a debugger attached and start monitoring for
significant location changes

3\. Minimize app

4\. Turn off iPhone

5\. Turn iPhone back on.

Now if you wait 3-5 minutes in the same spot, your app will be woken up and
will get a significant location change. Much simpler than driving and easily
reproducible in the office.

Now to know when the app actually wakes up while you're in the lock screen, I
added a local notification as part of the startup process. Now we just keep
the phone locked for a few minutes until it shows the local notification, then
you unlock it and see the logout problem.

Hope that helps everyone else!

~~~
jansen
Nice one, you guys are just smarter than us :)

If we had known that the bug was caused by reboots though, we probably would
have saved some gas as well.

~~~
mpetrov
Nah, it was something between a lucky hunch and laziness. The backup plan was
to test it an hour later when leaving the office for lunch but the restart
worked. I actually realized it had to do with significant location changes
early on since it was the new thing we added.

By the way, in general this isn't a reboot specific bug, the most secure
setting is actually not the "allow after unlock" one but the one that gives no
access to keychain if your phone is locked at any time. I noticed that
keychain starts refusing access 15 seconds after you lock the phone but most
apps keep their credentials in memory after they're read and the problem
appears at reboots only.

------
zaroth
The workaround is to allow access to the keychain data while the phone is
locked? I assume the Loom keychain data (including the users password) is only
accessible to the Loom app, just now it's accessible to the app even if the
phone is locked. Doesn't give me a warm and fuzzy feeling.

Why not store username and a random token in the keychain instead of the
actual password? You create the token on a valid login and keep it on the
server and in the keychain. Basically it's a session cookie.

~~~
zaroth
From Apple docs:

    
    
      kSecAttrAccessibleAlways
      The data in the keychain item can always be accessed regardless of whether
      the device is locked. This is not recommended for application use. Items
      with this attribute migrate to a new device when using encrypted backups.
    

If the data is always accessible regardless of if the device has ever been
unlocked, then by definition it must not be encrypted, or at least, it must
not be encrypted with any key actually derived from the user's password!

Consider, for example, iCloud backups. The "kSecAttrAccessibleAlways" data may
be encrypted, but its with a key that Apple knows. So using
kSecAttrAccessibleAlways sounds a lot like how Google backups store your WiFi
password.

As 'itsboncheck' mentions down-thread, a better choice (still a serious
compromise) may be...

    
    
      kSecAttrAccessibleAfterFirstUnlock
      The data in the keychain item cannot be accessed after a restart until
      the device has been unlocked once by the user. After the first unlock,
      the data remains accessible until the next restart. This is recommended
      for items that need to be accessed by background applications. Items
      with this attribute migrate to a new device when using encrypted backups.
    

That _sounds like_ whats happening is the value is stored encrypted in flash
in the protected area which is locked with the key derived from the user's
pin/password, which is what you want... But they must cache the plaintext
value in some temp storage to be able to provide it while the device is
locked. That's just my speculation, I don't see Apple divulging the source
code anytime soon...

Storing a token in the keychain is preferable, because revoking a token is
less onerous than nuking a user's password. Keeping the security set to
maximum (kSecAttrAccessibleWhenUnlockedThisDeviceOnly) also seems advisable
when we're talking about user password, or even access tokens. Just defer the
backup task until the device is unlocked.

If you absolutely must proceed with the background task while locked, consider
issuing a less-trusted token which is ONLY used for background tasks, and
provide the absolute least privileges possible to accomplish the background
task when presented with that token.

Also consider rotating it. For example, whenever a user connects with their
full-access token, indicating their device is unlocked, it's a good time to
expire the old background-task token and provide a new one. This also lets you
do a "mass delete" of existing tokens in case of any funny business, and
you'll have automatic recovery ("background processing will start working
again next time you unlock").

~~~
e28eta
> they must cache the plaintext value in some temp storage to be able to
> provide it while the device is locked.

From what I remember, no, they definitely don't save it in plaintext.

It's late, and I don't remember the exact details, but there are places where
Apple talks about how it works.

Here's the gist, I think: When the user unlocks the phone, several encryption
keys are generated using the pass code. One's used for Available After First
Unlock, and that one's stored in RAM till the device reboots. Another is used
for items that are only available when unlocked, and that's thrown away every
time the phone is locked. Items that are restricted to the device use a key
that is also derived from a private device identifier.

So not plaintext, but the decryption key is hanging around in the device.

You're definitely on target with the suggestion for a background task token
though.

------
pilif
I faintly remember the WWDC session (I wasn't there. I only watched the
videos) about background launching to talk about this exact issue. I also
remember thinking that this is going to bite people.

I agree with zaroth here: don't downgrade the keychain security for the
password. Either do nothing if the phone is not authorized or store a token in
more accessible security levels.

That's what the presenter in the session has recommended too.

------
itsbonczek
If you need keychain access while the device is locked, using
"kSecAttrAccessibleAfterFirstUnlock" is definitely recommended over
"kSecAttrAccessibleAlways." I don't remember where I heard this (possibly a
WWDC video), but I think most attack vectors require the phone to be restarted
to work. Maybe someone with more knowledge on iOS security can chime in with
details.

~~~
dunham
Or "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly" if you're ok with the
secret not transferring to another device. This prevents the secret from being
exposed in a password protected backup or an iCloud backup.

------
brandoncor
Oh man. I seriously love you. I've been trying to find a fix for this for
about two years now.

My app sometimes launches from the background after the user enters or leaves
a region. It looks for a session token (not a password) in the Keychain and
would return nil and log the user out. I'm not sure how I never came across
kSecAttrAccessibleAlwaysThisDeviceOnly in all my hours trying to find a
solution.

By the way, this isn't specific to iOS 7. I've had the same issue since iOS 5.

------
e28eta
I have a problem with the downgraded protection on the password, but the abuse
of significant location change notifications to start background http requests
bothers me a lot more.

I hope a follow up post discusses how they removed this "feature" and took
advantage of the new API for background updates.

~~~
jansen
It's an opt-in feature, well described in the app and per default turned off.
We are however implementing the new iOS7 background uploading processes as
well.

~~~
e28eta
Fair enough. Opt-in is way better that opt-out, or worse: not user
controllable, and I hoped that was the case when writing my original comment.
However, I was doubtful that the ramifications could clearly be explained and
understood by most users. But, I haven't tried to do so myself, nor have I
looked at the app, so I apologize for the strong stance.

As a user, and an iOS developer, I'm excited to have a supported, battery-
friendly (intelligent?) system for allowing frequently used apps to update in
the background. And this seems like a good fit.

------
baddox
Is there no way to put iOS devices into a debug mode and send fake location
data? Android has supported that for a while, and it's much nicer than
actually driving around.

~~~
robterrell
You can definitely create any number of locations and switch around between
them in the Simulator, triggering the significant location change notification
without driving around town. Although in their story it was the chatting-
while-driving that actually led to finding the problem.

------
andyhmltn
Interesting. I've been having this issue with SnapChat recently. It no longer
remembers my login. I assumed it was just an issue with the app and not
something bigger.

------
supercoder
If someone presented this fix on my team they'd be fired.

~~~
jc4p
Sadly you don't say where you work on your bio so I can't add your company to
my "never ever apply at" list.

New OS updates introduce weird bugs that require hacky code to get around.
Early adopters will always update to them and then complain to you that your
app is broken, making the hacky code required.

~~~
muzzamike
This hacky code most certainly isn't required.

