If you’re a software developer and haven’t yet heard of the recent high-profile security flaw to plague the internet, then my heart bleeds for you and you should go read about it right now.
The defect in the OpenSSL library that is responsible for handling a staggering amount of global network traffic has caused a panic close to Y2K proportions. But, the concern has proven to be legitimate this time. The possible ramifications of this flaw range from an attacker being able to obtain private SSL keys to systematically obtaining private user data — very concerning on all fronts.
Now that the cyber security dust has settled and the internet is once again a “safe” place to play, it’s a good time to ponder what we as developers are doing to ensure the security of our software while still maintaining a high bar for user experience. Even in the face of an internet that yields a seemingly endless supply of jerks trying to steal their data.
1. Minimize Personal Data Collection
No one can steal something from you that doesn’t exist.
The first thing you can do to minimize the risk of your users’ information being snagged by nefarious people is to not collect it in the first place. Think very carefully about why you need a given piece of data, whether it be a location, contact, or photo, and try to determine if there is a way to get what you need without permanently storing that data.
Oftentimes, developers tend to be hoarders of information, in part due to the ease of which that information is stored (SQL inserts are easy and storage is cheap), but in many cases this data collecting can come back to haunt you as it did for the app Path in 2012. In that case, Path was transmitting and storing all of your contacts in order to inform you of other Path users you might know and might want to connect with. However, as Matt Gemmell explains in this very well written post, storing contact data in the clear for this purpose is totally unnecessary. And if you see “2012” and think to yourself, “Surely we must be beyond these sorts of shenanigans by now,” remember this similar discovery just this past year about the popular app QuizUp running afowl of users’ personal data in an even more haphazard manner.
2. Provide Clarity
If you absolutely must collect personal data, honesty is always the best policy. iOS has a very well-defined facility for informing your users why you’d like to snoop around in their private information (see “Cocoa Keys” in this plist key reference). Simply specify the appropriate key for the service you wish to access and a friendly message as the value, then, when iOS prompts for permission, the user will see your descriptive text instead of a vague default message.
3. Be Secure
Once you’ve decided what personal data to ask for and how to ask for it, you need to ensure it stays private within your app/service. Keep in mind, entire books have been written on this topic, so I am in no means declaring myself the supreme authority on the matter. Bearing that in mind, here’s a very high level list of “well, duh” sort of security considerations you should remind yourself of as you create/audit your app/service.
- Use iOS file data protection (see Protecting Data Using On-Disk Encryption) for sensitive data. It has been available since iOS 4 and is the first line of defense for sensitive data provided to you by your users that you might store on the device file system.
- Always store user credentials in the device keychain. It’s the best way to ensure passwords, tokens, and the like are never accessible in plain text, and libraries like SSKeychain make this a snap.
- Use SSL (HTTPS) for any client-server communication that includes sensitive data. This is inherently slower than sending data in the clear, so if performance is a concern, you may want to be selective about which calls go over SSL. As mobile adoption continues to grow and we spend more of our time on the move, we are connected to a greater number of potentially untrusted access points, thus increasing the likelihood of man-in-the-middle attacks.
- Prefer a token-based authentication system over HTTP basic auth. OAuth 2.0 has quickly become the industry standard. It allows for token expiration/invalidation and doesn’t require a user’s login information to be sent on every request, thus granting you a great deal of recourse in the event of a breach.
4. Plan for Failure
One of the more difficult aspects of securing an app is maintaining a great user experience in the face of failures. In many cases, failure may be unexpected and difficult to plan for. However, in the case of security, your application code can and should be architected to handle an unexpected logout situation.
Handle Unexpected Log Out
In the event of a security breech, you may decide to invalidate all active tokens or even to force password reset. For the people using your app at the exact point in time at which this occurs, this has the potential to be a poor experience. Ensure that your app doesn’t assume that the only time log out occurs is when the user taps a “Log Out” button or otherwise expresses specific intent to log out. In practice, this means a few things:
- Your login flow must be presentable from anywhere in your app, so consider this as you design the root navigation structure. How you present the login flow on first launch or on log out may need to be different than in the event of a forced logout.
- In your web layer, you’ll want to specifically look for credential invalidation scenarios (these will vary depending on your particular security model, but in general will be indicated by a specific HTTP error code), and trigger a log out. Since this can occur as a result of any request, consider integrating it into your web pipeline as a single point through which all responses are processed. If you’re using AFNetworking, this can be easily done by implementing a custom subclass of
AFHTTPResponseSerializerand overriding the
- In order to trigger the logout, your view layer needs to know about the request failure that has occurred in your web layer. A good way to keep concerns separated in this case is to have your web layer post an
NSNotificationthat indicates a log out has occurred and for your view layer to listen and respond appropriately.
Since any request can be interrupted at any time by a credential invalidation event, it is important to consider the potential ramifications to data integrity. In general, a good rule to follow is that any non-idempotent requests should be high priority and eventually succeed, even in the event of potential failures. A
GET that retrieves a list of status updates does not change any state or represent any data entry, therefore it is OK to let that fall on a user to retry after a forced logout. Alternatively, a user posting a new status update represents the intent to change state by saving some amount of data, and in this case, a forced log out event should not require that user to reenter the entire dataset and post again.
One way to ensure this level of data integrity is to encapsulate everything about each request into some sort of object that can either be retried or copied and re-executed. This object can be saved in memory, or even to disk if the content is important enough, and then replayed upon successful login. AFNetworking’s
NSURLConnection implementation provides this abstraction as NSOperation subclasses that represent discrete network operations, and while NSOperation does not support replay, you might implement your own custom subclass with copy to allow for this. Likewise, its
NSURLSession implementation directly exposes each
NSURLTask which you can similarly use to provide some form of replay.
Providing network operation replay may not be trivial and there is by no means a one size fits all solution, so be pragmatic about how much effort you dedicate to this sort of functionality.
Finally, one of the most important aspects of handling security with user experience in mind is communication. Regardless of the action, if it is something forced upon your users, you should be transparent about what is going on. Do not force reset passwords or revoke tokens without some sort of messaging that explains why you’re doing it. Push notifications are a great way to ensure that this messaging is seen and it means that only active users that have the app installed will see it. An alternative would be to communicate via email, and if your app represents a service that requires email on sign up, this may be a more reliable way to get in touch with users.
Hopefully you have been able to glean at least one piece of helpful advice around how to ensure a secure app or service while maintaining a delightful user experience.
As our computing becomes more and more mobile, the challenges around continuing to provide secure experiences becomes more and more difficult, so I encourage you to always have it in the front of your mind as you are designing and developing the next earth-shattering app.
If you enjoyed this article, please come back for more inspiring content from the Raizlabs team every week. I also provide additional ramblings on Twitter: @nickbona.