I’ve been sifting through this year’s WWDC videos looking for all of the interesting bits around security & privacy. I’m not anywhere close to being done. Fortunately Luis Abreu has done the hard work for all of us and compiled his findings into a very handy post. The post has a lot of great info for developers, QA, and designers around what’s new and what’s changing. Of course you’ll still want to go do your own research before implementing any changes, but Luis’ post serves as a great quick-start guide.
A few weeks back there was an abuse of Apple’s Enterprise Developer Program that gathered a lot of attention after it was used to distribute a GameBoy emulator outside of the App Store. Obviously the focus at the time was on the fact that anybody could build an app to distribute outside of the App Store by using one company’s enterprise certificate. What got no attention, as far as I can tell, was the lack of security on the build server.
Secrets of an Error Log
Not quite. When my boss, Jay Graves, went to try out MacBuildServer his build failed as a result of a required variable that had not been set. When a build fails in Xcode, you’ll get output telling you what went wrong so you can try and fix it. Many continuous integration servers, like Jenkins for example, will give you console output from the entire build process so you can see everything that took place and any contributing factors that may have led up to the build failure, as well as the error that ultimately caused it to fail. MacBuildServer provides the same. If a build fails, you’ll get the option to view the error log. One of the first things Jay noticed was at the top of the log, the build server had echoed out the password for the certificate that the server was signing with. We had a laugh about it, but without the certificate itself the password doesn’t do you much good. But what if we could get the certificate?
I created an empty application in Xcode. In my project settings, I navigated to the Build Phases tab for my target. Under this tab you’ll find an item called “Run Script”. This does exactly what you might expect; it will execute any commands you enter as part of a shell script during the build process. I started off with a simple
whoami just to see if it would work. I followed this with a non-existent command in order to guarantee my build would fail right after my script had run so I could see the output. I uploaded my sample project to GitHub, pointed MacBuildServer to it, chose the option to sign with their enterprise certificate and as expected, my build failed. When I clicked to view the error log I was given the server’s output from my build and at the end of it was the output from my
whoami command showing me the user that the build had been run by.
The output of
whoami wasn’t terribly interesting, but what it represented was rather significant. At this point it’s pretty much game over for the build server. I have the ability to run any command that I could as a normal user logged in to the computer. The build failure logs told me everything I needed to know about where the enterprise certificate and corresponding keychain were being stored. Combine this with the certificate password that the build log is already giving and you now have the ability to locally sign any app you want with this company’s enterprise certificate. Of course this is less meaningful now that the certificate has been revoked. However, you could also run any number of other commands to cause harm to the build server or use access to the server to launch other attacks from inside their network. From navigating the filesystem, to copying files, to tampering with other people’s git repos, the possibilities are just about endless. I promptly contacted MacBuildServer about this problem and they have since disabled scripts from Build Phases and Build Rules.
What It Means for Users
While this seems like a lesson for administrators of hosted build servers, this should also serve as a warning to users. Using a hosted build server requires that you trust a third-party with your code, all of your signing credentials, as well as trusting that the IPA you get out the other end has not been tampered with. In the case of this particular service, the GitHub repository you use has to be public, but a number of other hosted build server solutions will build from private repositories. If the server you entrust isn’t properly secured, you may unwittingly be giving access to your code to a malicious party.
Of course, this is true of any third-party service. We often hand sensitive information over to a third party and trust that they have properly secured their services to protect our information. But build servers may inherently have a greater risk of being exploited. The very nature of a build server requires that it downloads remote code that is controlled by a user. Usually this is exactly the kind of action that you would want to try and prevent your users from doing on your servers.
Additionally, build servers are usually set up in trusted environments. When an organization sets up a build server internally, it usually has a trusting nature because the need for convenience outweighs security needs. A build server hosted inside of an organization, on a private network, to compile and run code that was written by developers who work for that company will naturally have less need for security than a build server accessible from the Internet to anybody who wants to sign up for a free account. If you’re building the latter, a fair amount of work will need to go into locking down the build server to limit access; that’s not the environment they’re usually set up for. It’s not impossible, but you need to know what you’re doing.
All of this is to not say that hosted build servers should not be used. Developers should simply be aware of the potential risk involved when trusting a third party like this. Similarly, anybody offering hosted build servers as a service should be aware of the risk involved, and be familiar with the security measures necessary to protect themselves and their users.
This post should also not be construed as a strict criticism of MacBuildServer. When I contacted MacBuildServer about the issues I had found, they were receptive and appreciative to my feedback and promptly addressed the issue. I have not taken the time to investigate other hosted build server solutions, but I would be surprised to learn that other services don’t suffer from their own security problems.
Special thanks to Carl Veazey for his help in Xcode and eternal willingness to answer my stupid questions.
A lesson learned a long time ago in the world of desktop computing is that a server can’t trust what a client tells it. If a user goes to log in to your server, your server checks it, you would never leave it up to the client-side application to tell you the user is authenticated because you wouldn’t know if it’s telling the truth. You have control over your server and how it behaves, which is why it should be left in charge of making important decisions, and providing the client with just enough data to execute on those decisions.
This lesson is still being learned in the mobile world. One of the latest examples is Simple Bracket; a very nicely designed iOS app for choosing your March Madness bracket. When playing around with Simple Bracket, a couple of things jumped out at me. The first is if you sniff the app’s traffic, you can see that when searching for Pools, the server responds with a data for a list of matching pools, including their PIN. This seems to allow the app to quickly, and cleanly either accept or reject your PIN when you try to join a pool. The obvious downside to the client side checking here, is anybody can see the PIN for any pool, rendering the PIN completely useless.
The second thing I noticed is the way Simple Bracket determines when it should show certain data. Currently if you go to view another user, the app tells you that you won’t be able to see their bracket until March 21st, when March Madness begins and brackets are locked. This data is kept private from other users so that nobody can copy somebody else’s bracket. Normally in a situation like this, you would want to have the server be the one to determine when this data should be displayed, and not make the data available to the client until that time. In Simple Bracket they decided to let the client determine when to show this data and the server seems to happily provide it to the client. As such, by simply changing your device’s date to March 21, 2013, you can see anybody’s current bracket.
This is just one more example in a trend we see where many developers view iOS as a closed system that they can trust, and it’s not. As mobile platforms mature and develop, we should be cautious to not repeat the same mistakes that were already made on other platforms. There’s no need to repeat these mistakes and endure the same pain and consequences rather than learning and growing from the past.
Congratulations to all of those who won Appy Awards this year. A very special shout-out to Fandango who somehow won an Appy for Mobile Payments, despite not properly securing customer credit card information. Fandango’s app allows self-signed SSL certificates; combine this with the fact that they transmit all of your credit card details in plaintext (though over an HTTPS connection) when you check ticket availability, and it’s a bit ironic that they would win an award for this category. More than two months after being contacted about these issues, they have yet to respond or do anything to try and secure their customer info. Well done, Fandango.
Update 3/17/13: As reader iOSSneak points out below in the comments, Fandango seems to have fixed the SSL issue in version 5.5.1 of the Fandango app, which was released the day after this post. The Fandango app no longer accepts self-signed SSL certificates.
Related post: iPhone Apps Accepting Self-signed SSL Certificates
There was a lot of fuss last week about an alleged iPhone lock screen bug that allowed an attacker to bypass the lock screen and access your iPhone’s filesystem. I wrote an article on iMore explaining that this wasn’t the case and the confusion seemed to be the result of a misunderstanding on how iPhone passcodes work.
As mentioned in the article, when an unlocked iPhone is plugged in to a computer with iTunes running (or a locked phone that you then enter the passcode on), iTunes has a mechanism that allows it to access the device’s filesystem in the future without requiring the user to enter their passcode. I ambiguously referred to this “mechanism” because I had absolutely no clue how it worked. All I knew is that the first time you plug a locked device in to a computer, iTunes will give you an error message. However, once you enter the device’s passcode, iTunes will never prompt you again to enter it on that computer. Whether the phone is locked or not, iTunes (and any other app that can talk to your iPhone) will gladly read files from the device and write new data to it.
Twitter user Hubert was kind enough to send me more info about this process. He sent me an article that looks at how iOS 5 backups work. The article briefly explains the use of Escrow Keybags by iTunes (technically I think usbmux is what uses them) to talk to locked devices after they have been unlocked once. The gist seems to be that there are certificates and keys which are exchanged via iTunes when an iPhone is plugged in and unlocked, which are then stored in an Escrow Keybag on the host computer. The Escrow Keybag for every device you’ve ever plugged in to your machine can be found in /private/var/db/lockdown and is named with the UDID for the device that it is for. When a device is plugged in to your computer in the future, this Escrow Keybag is used to unlock the device without the need for the user to enter the passcode.
The confusion on the alleged lock screen bypass bug came from this. The bug discovered simply made the screen go black with a status bar still visible. If you plugged this device in to a computer that it had previously been plugged in to while unlocked, the computer used the Escrow Keybag in order to read the file system without needing the passcode. This would have happened with or without causing the black screen glitch. If the person who found the glitch had plugged their iPhone into a computer that it had never been connected to, they would have gotten an error from iTunes saying that the device was locked and they would need to enter their passcode.
The original video demonstrating the bug can be found here. A video of the same bug being reproduced, but with the phone plugged in to a computer it has not been unlocked with before can be seen here. My sincerest apologies that I don’t have any block rocking beats accompanying my video.
I recently spent some time looking at a number of iPhone apps in the App Store to see how well they were implementing SSL. It was a little surprising to see how many big-name apps ignored SSL errors and even more surprising to see some that didn’t use SSL at all. If you want the short version, head on over to iMore.com. Here I wanted to take some time to take a closer look at the issues that I found and how I found them in hopes that other developers can avoid making the same mistakes.
Charles Proxy is a tool I’ve discussed on here previously. It’s a proxy tool that allows you to intercept traffic from your device. One of the features Charles has is SSL proxying. iOS ships with a listed of trusted Certificate Authorities on every iPhone, iPad and iPod touch. These CAs are organizations, who are responsible for issuing SSL certificates, that Apple has deemed trustworthy. Modern browsers, as well as other software and hardware, ship with similar lists that tell them which SSL certificates can be trusted and which cannot. If an iPhone, for example, goes to make a secure connection to https://google.com, the device looks at the SSL certificate and, among other things, checks the CA that issued the certificate. In Google’s case, the device would see that the SSL certificate has been issued by Verisign, and since Verisign is a trusted CA, it will accept the SSL certificate for this communication.
Conversely, if you were to try to connect to https://insecure.skabber.com in Safari on your device, you would receive an alert about not being able to identify the server’s identity and asking if you want to continue anyway. This is because the certificate has been issued by the Ministry of Silly Walks, which is not a trusted CA. If an app attempts to make a connection and runs into an issue like this, that app should fail the connection. Since the identity of the server you’re connecting to cannot be trusted, the application should err on the side of caution and refuse to send any data, lest it turn over sensitive details to a malicious entity. To understand why the warnings should be heeded or what might cause them to appear, it may be helpful to give more background here.
The reason encryption works is because a person trying to eavesdrop doesn’t have the keys to decrypt the data. But what if they did? In a man-in-the-middle attack, a malicious third party sits between you and the server, decrypting your traffic. The attacker pretends to be the server to you, and pretends to be you to the server. When you send your encrypted data to what you believe is the server, it’s actually the attacker who now has the keys to decrypt the data, because you’re the one they began the connection with. After decrypting the data, looking at it, and manipuulating it if they want, the attacker they re-encrypts the data and sends it across their secured connection to the actual server. When the server reponds, the attacker will be able to do the same, decrypting the data, looking at it, then re-encrypting it before sending it along to you. But in order to do this, the attacker would need to possess an SSL certificate for the server you’re trying to connect to.
As it turns out, anybody can create their own certificate authority. As such, anybody can issue an SSL certificate. Furthermore, that SSL certificate can be for any server that they want. You could go generate an SSL certificate for google.com right now if you wanted. But what would prevent you from ever being able to make use of that certificate, like in the scenario described above, is that it would not be issued by a trusted CA. You could say you were google.com, but nobody would believe you because your certificate didn’t come from a trusted source. SSL hinges on this chain of trust. And this is where some apps fail.
Instead of only accepting SSL certificates from iOS’ trusted list of CAs, some apps accept SSL certificates issued by anybody. Because of this, the man-in-the-middle attack described above is possible against traffic sent by these apps. An attacker can use an SSL proxying tool that will generate SSL certificates on-the-fly for whatever server is trying to be connected to. Since the apps trust any CA, they will allow these certificates and open up communication with the attacker, who can then view the traffic and send it on to the server. Credit Karma, Fandango, Cinemagram, Flickr, eFax, WebEx, TD Ameritrade, E*TRADE, Monster, H&R Block, ooVoo, Match.com, PAYware (by Verifone), EVERPay Mobile POS, and Learn Vest all suffer from this issue. Some companies have already responded. E*TRADE already has a partial fix available, and a more complete fix on the way. Credit Karma, TD Ameritrade say they have updates in the works. Cisco has opened tickets in their bug tracker (Cisco.com login required) and CVE for users to track the issue, and are also working on a fix. Monster responded but has not been able to say whether or not they will fix it. eFax’s stance is that once your data is outside of their network, they cannot protect it. None of the others have responded. Users should be particularly careful with buying tickets through Fandango since credit card details are transmitted.
These apps are easy enough to find on your own by using a tool like Charles Proxy or mitmproxy. Normally for these tools to work for testing, you would install a certificate on your device to tell the device to trust your CA. Simply skip that step (or uninstall the certificate if you already have it Settings > General > Profiles) and fire up your proxy with SSL proxying enabled. With traffic going through your untrusted proxy, you can fire up apps like Facebook and see that they won’t work. Looking at the requests in your proxy, you’ll see that a connection was attempted, but then closed and no traffic was sent. Now, if you go and open an app that ignores warnings about untrusted CAs, like Flickr, and try to log in, you’ll see something different. You’ll see the app successfully log in and in Charles, you’ll see the request that was sent to the server, and inside it, your username and password. If an attacker were performing a man-in-the-middle attack on you, this is what they would be seeing.
While searching for apps with this problem, I stumbled upon a few that while not suffering from this, had other security problems related to logins. Flixster, iSlick, Camera Awesome!, TechCrunch and Photobucket all send some login information over HTTP. In the case of Camera Awesome!, it’s only Photobucket credentials, and in TechCrunch it’s only authentication for Pocket. But iSlick, Flixster and Photobucket all send usernames and passwords for their respective accounts in plaintext. This means that a user wouldn’t have to do any SSL certificate generation or proxying, they need only be on the same network as you and sniff the network traffic. Obviously this poses an even larger security risk than just accepting self-signed SSL certificate. Particularly if users use the same usernames and/or passwords for other accounts. In Photobucket’s case, they hash your password with MD5, but the password can easily be retrieved using a reverse MD5 hash lookup tool.
So what should be done? Well in the case of apps not using encryption, they should be using SSL for those login requests. iSlick says that SSL support will be coming in a future update. I haven’t heard any updates from the others. As for the other apps that are using SSL, but not implementing it correctly, they need to make sure that they respond to SSL warnings properly. From what I’ve seen, most networking libraries refuse self-signed SSL certs by default. Perhaps ignoring these warnings was done for development purposes and developers forgot to later pull that code out. Whatever the case, I hope each of the companies will respond quickly with updates for their users and hopefully we can all learn from their mistakes to be a little more cautious about how we implement security in our apps. If you decide to do some research on some of the apps you use, be sure to let developers know if you find a problem so they can fix it.
After looking at the app bundle with PhoneView and failing to find anything interesting there, I fired up Charles Proxy to have a look at the app’s traffic. The good news is Simple appears to be sending everything over SSL. This means that if, for instance, you’re at the airport and your iPhone is on an open wi-fi network, an attacker will just see encrypted garbage if they look at the traffic going between your iPhone and Simple’s servers. Now the bad news.
Charles Proxy has a wonderful option called SSL proxying. SSL proxying is handy for debugging when your app’s traffic is encrypted, but you want to see the contents. The first issue I noticed with Simple is it doesn’t take any steps to make sure it’s talking to its own servers. SSL guarantees that your connection is encrypted, but it doesn’t necessarily guarantee who is on the other end of that encrypted tunnel. Without the app validating that it’s talking to the server it should be talking to, it’s possible for an attacker to perform a man-in-the-middle attack.
Editor’s Note: In iOS (and any modern web browser), there is a list of trusted CAs that is bundled with the OS. These CAs are who issue SSL certificates. When an SSL connection is established, if the SSL certificate’s URL does not match the URL that a connection is being made to, a warning will be raised and the connection will fail. This means that a malicious attacker can’t use his SSL certificate that he may have acquired for fakebank.com to perform a MITM attack with requests that are going to https://simple.com. Some sort of compromise of the chain of trust in SSL would have to take place, like a CA being hacked and generating a phony certificate for Simple’s servers. When I originally said that Simple doesn’t take any steps to make sure it’s talking to its own servers, what I meant was it’s not performing any validation outside of what SSL inherently gives on iOS. There is an additional step that can be performed that is called SSL pinning. With SSL pinning, the app can take matters into its own hands to perform additional validation on who it’s talking to, rather than relying on the more general list of trusted CAs. This would ensure that in cases like a CA being hacked, there would be an additional layer of security by the app to validate it’s still actually talking to Simple’s servers. However, it’s worth emphasizing that without such a compromise taking place, user data is encrypted and secure with Simple’s app. All data is going over an SSL connection, and some compromise of SSL would need to occur for this data to be at risk in this scenario.
Normally the way SSL works is the client will setup an encrypted connection with the server before sending data to it. The way a man-in-the-middle attack works is the attacker intercepts your communications to the server and the server’s communication back to you. The client has an encrypted tunnel set up with what it believes to be the server. The server has an encrypted tunnel set up with what it believes to be the client. In reality both are just talking to the attacker and the attacker relays the traffic on after it’s able to take a look at it.
A MITM attack isn’t extremely likely on iOS because iOS ships with a list of trusted SSL certificate authorities. Using an app like Charles Proxy, you use a self-signed SSL certificate which iOS won’t trust unless you explicitly tell it to. That said, SSL is not foolproof and there have been instances in the past of it being exploited, and even of attackers creating phony SSL certificates signed by a legitimate and trusted CA.
Ultimately this isn’t something users have to worry about too much, but it is something Simple should fix. The app should be making sure it’s talking to Simple’s servers and Simple’s servers should make sure that it’s really the app that’s talking to them.
What’s more interesting is what you find once you start looking at Simple’s traffic. Since Simple isn’t doing any certificate pinning, we can enable SSL proxying in Charles and watch the traffic that Simple is sending and receiving. The first thing that jumps out is the request to https://api.simple.com/user-api/mobile-auth-tokens when you sign in to your account. Included in the request are your plaintext username and plaintext passphrase. The request is sent over SSL, but this doesn’t guarantee security and when dealing with such sensitive data, more security measures should be taken.
To be fair, up until this point, I haven’t seen anything that Simple is doing wrong that other banks are doing right. Of the two other banks I have accounts with, neither do pinning of SSL certificates or have any additional security to protect your username and password when signing in. So far Simple’s behavior seems par for the course. So far their security is just as disappointing as my other banks.
But wait, there’s more… There’s another request sent when you sign in: https://api.simple.com/user-api/users/UUID. The body of the request isn’t all that exciting, but the contents of the response are. It seems to include all of our personal info including full name, email, street address, date of birth, and social security number. Yes, your social security number. Simple’s server is sending all of this data back to your app when you log in, and as most of this info doesn’t seem to be used by the app anywhere, it’s unclear why they feel the need to send it.
After reading their security policy I felt hopeful that they’d be receptive to my concerns, so I sent an email to their security team. After eight days, I sent a followup to make sure my original email hadn’t been lost. Finally, a reply:
My apologies for not responding sooner; we did receive your report and I’ve discussed the issues you’ve raised with the team.
We are continuously working to improve the security of our solutions, and you can expect to see steady improvements as we continue to build out our platform. The challenges with ensuring the effectiveness of SSL are well-understood and under constant review inside the organization. In addition, it does look like we are transmitting more data than we absolutely need to and that is something that we are actively working to improve.
Thank you again for your message, and please do not hesitate to contact us again. We also provide a GPG key for communicating security issues with Simple on the Security page at www.simple.com; please feel free to use that in the future if you are able.
Justin Thomas Director of Information Security Simple
So it sounds like maybe they’ll fix this at some point… or maybe not, I’m not sure. I’ve seen restaurants respond with more urgency when they screw up my hamburger. For a company that I’ve entrusted with my personal and financial data I expect them to treat security with the utmost importance. I expect them to fix security oversights as quickly and apologetically as possible. Since the company does not seem to share my enthusiasm there, I felt that the responsible thing to do was inform other users of these shortcomings so that they can make an educated decision on what to do with their own accounts. I’m still hopeful that eventually Simple will fix these issues, but in the meantime their users deserve to know how the company is handling their information.
Update #1 12/20/12 3:00PM – Simple has responded to several users on Twitter in order to try and address their concerns. Happy to see them actively responding to users and looking forward to seeing the issues actually fixed.
Update #2 12/21/12 9:00AM - I received a tweet from Brian Merritt, the Director of Engineering at Simple, asking if I’d be willing to give him a call and talk.
Good news. The SSN issue has been fixed as of last night. If you go look at your traffic now, your SSN is no longer being sent back by the server. Brian also reassured me that the SSL certificate pinning is an issue that they were aware of and have been working on. Unfortunately that sort of fix will require an app update, so it’s something we’ll have to wait on, but it does indeed sound like the fix is coming.
There has been a lot of talk this week about hacker Alexy V. Borodin (who goes by the handle ZonD80) who put up a service that facilitated illicit transactions of IAP (in-app purchase) content from iOS apps. While identifying security problems with the way IAP transactions work, it also underlined the need for developers to be diligent in secure practices with their apps.
The exploit requires users to first install two certificates on their iOS device: an SSL certificate for Alexy’s “fake App Store” and a certificate to trust the CA that signed that SSL certificate. Once this is done, the user changes their DNS settings to point to Alexy’s DNS servers. Now when an IAP request is made, instead of talking to Apple’s servers, the user’s device is talking to Alexy’s, and instead of being charged then given a valid receipt, the user is not charged and Alexy’s server simply hands back the receipt it has cached. As far as the app on the user’s device is concerned, the user has paid for the IAP content and the app received a receipt from Apple verifying this.
There are two problems (as far as I can tell) with Apple’s current implementation that allow this to happen. The first issue is that a user can trust 3rd party certificates for in-app purchases. If StoreKit had been locked down to only trust Apple’s certificates for this type of traffic, then the receipt likely never could have been sniffed and cached by Alexy in the first place. Even if he had acquired them somehow, users would be unable to install his SSL and CA certificates that are required to perform the fake transactions. However, since users have (and in many legitimate cases need) the ability to install 3rd party, untrusted certificates in iOS, I don’t know if it’s possible for StoreKit to ignore the OS-wide certificate list, and only use its own provided by Apple.
The second problem with Apple’s implementation (as pointed out by Alexy) is that the receipts have no uniquely identifying information. The receipts aren’t tied to any particular device or Apple ID. This means that only one person ever has to buy the IAP content, but they can then share that receipt with others who can re-use it to fool apps on their devices into thinking they paid for the content. If Apple were to only fix this part of the problem by having apps check that the receipt belonged to the current device, it would still leave the potential for users to recycle their own receipts over and over. For example, if a user were playing Angry Birds and decided to buy the $0.99 IAP for 20 Space Eagles, they could capture the receipt from the transaction, then re-use it on subsequent transactions for the same in-app purchase. So by paying $0.99 one time, they could acquire unlimited Space Eagle in-app purchases.
All of this boils down to the fact that we have a very broken in-app purchase system right now, so how do we fix it? The bad news is, we really can’t; Apple has to. Signs already point to Apple taking measures to fix the system. They’ve started including a unique identifier in App Store receipts, which apps could potentially (with an update) use to validate that the receipt does actually belong to that device. I’m not sure if they have any plans to lock down StoreKit to make sure it only ever talks to Apple.
While developers can’t fix this broken system, there are steps that can be taken to minimize the risk involved. In fact, developers who follow Apple’s instructions on validating receipts against their own servers seem to be unaffected so far. When this story first broke it was thought that developers who were validating receipts against their own servers were immune, which isn’t quite true. Receipts validated against developer servers can be forged in the exact same way. Anybody can capture the response from the developer’s server that occurs during the validation, and then reuse it. However, to my knowledge, this isn’t currently being done. While it’s not a solution, it does provide one more hoop to go through for people to steal IAP content; a hoop which Alexy doesn’t seem to be jumping through as evidenced by the large number of users on his website who complain about apps with which his service does not work.
It looks like Alexy may soon offer the same circumvention services for Mac App Store apps (then again, maybe not). Apple has been making efforts this week to minimize the damage done by Alexy’s service, will surely have a complete solution available shortly, and are hopefully already ahead of him with the Mac App Store and working on a solution there as well. In the meantime, developers should at least make sure they’re validating receipts agains their own servers, and if they can’t, look into a 3rd-party solution like Beeblex.
Update: Apple is now providing best practices to developers on closing this loophole. More info here: http://www.neglectedpotential.com/2012/07/in-appstoh-yes-they-did
Update 2: The service has now gone live for stealing Mac App Store IAP: http://184.108.40.206/osx.html
With iOS being a relatively closed system, it’s easy for developers to get lulled into a false sense of security; believing their apps are a black box that users can’t look into. Exploring a few different apps from the App Store, it’s clear that some developers either don’t realize that users can explore their app bundles, or they simply forget this fact. Using a tool like PhoneView or iExplorer, we’re going to peek inside app bundles and look for curious bits that developers may not have intended for others to see.
When exploring app bundles there are generally three different categories of files that are of interest and which developers should be careful with:
- Files that can be manipulated by a user to affect an application
- Sensitive user data
- Sensitive company data
User Manipulated FilesFiles in this category are usually some type of data store that hold information about how an application should function, or the current state of the application for a user. To see an example of this, go install Pocket Planes, run it on your device, then:
- Plug your device into your computer and fire up PhoneView.
- Navigate to Pocket Planes/Documents/playerData. In there you’ll find the file user-playerData.db.
- Double-click it to save it to your Documents folder. You’ll need an app that can read SQLite 3 database files (my personal preference is Base 2).
- Open user-playerData.db in your preferred SQLite app.
With that done, let’s look at another interesting table; playerMeta. Looking at some of the keys and values in this table, it becomes quickly apparent that this is where all of the player’s game data is stored. It stores how many coins you have (“b”), how many bux you have (“c”), your level, amount of experience, and a few other interesting bits in this table. By now you might see where this is going. You can edit the values in here and change something like your coins to have a value of 9,999,999. Then simply drag this newly updated .db file back onto your device in the same playerData directory you got it from, force quit the application on your device, relaunch, and voilà! You’re rich. There’s more fun to be found in this app, but I’ll leave that as an exercise to the curious reader.
So what’s the impact here? We have a free application that presumably makes money from in-app purchases of bux, with which users can buy coins to advance more quickly in the game. But because the value of both the coins and the bux is stored unsecured in a database, any user can go modify these values and avoid having to pay for anything. The impact to developers here is the potential loss of revenue. Additionally in multiplayer games, there’s the possibility for some users to cheat (as once was the case with Hero Academy).
With these types of files, you have a few options as a developer. If you don’t expect the data to change, or any changes could be handled with an app update, then store that data inside of your compiled code. This ensures the data is in the signed binary, out of the user’s reach. If you must update these values regularly, you can implement some kind of a checksum for your application to validate. For an example of this you can look at Temple Run. They store a lot of the user’s data (like coins) in a plain text file, but it’s also checksummed and if the checksum is not correct, the game ignores that data. One more option would be to store these values securely on or validate against a server. Obviously this requires more planning, upkeep and resources than the other options.
Sensitive User DataIt wasn’t that long ago that Facebook and Dropbox caught some flack for insecurely storing user authentication tokens. Basically the insecure storing of these tokens meant that a malicious user with physical access to somebody else’s device could gain unauthorized access to the victim’s account. Less than ideal. Unfortunately, there is no shortage of other apps out there with similar insecurities.
One such app that I recently came across is My Secret Apps Lite. This app claims it will securely store photos, videos, bookmarks, browsing history, notes and contacts. It also has several mechanisms intended to inform you if somebody tries to gain unauthorized access. It even goes as far as letting you set a decoy pin so that if you’re forced to tell somebody your pin, you can give them the decoy which will show them your decoy data.
So how does its security hold up? As you may have guessed, not well at all. Not only can all of your “secure” data be accessed in the Library/Application Support directory (bookmarks, history, photos, notes, security log, it’s all in there), but by simply looking at Library/Preferences/my.com.pragmatic.My-Apps-Lite.plist, you can see the pin and decoy pin (if there is one set). In reality, this app provides more novelty than actual security.
Other apps might store most of your sensitive data securely, but still leave some of it unprotected. For instance with Mint, I couldn’t find anything along the lines of account numbers or login credentials, however, it does look like they store your transaction history in a plaintext database. They made the effort to secure other data in the application, so I’m not sure why this data was left in unsecured.
Chrome is another application that stores most of your datas securely, but not all of it. They made good efforts with decisions like having password encryption turned on by default, but one file that is not stored securely is Library/Application Support/Google/Chrome/Default/UIWebViewCookies. If you have one device where a user has logged in to their Google account, then you copy that Default directory of that device to another, after relaunching Chrome, you’ll be able to navigate to any Google website and be logged into the user’s account.
You can also glean such details as what websites the user has saved their login info for, as well as the usernames for all of those sites. While they’re not storing your password in plaintext, they’re leaving enough info on the table to make you (or at least me) uncomfortable.
What makes me more uncomfortable is Google’s philosophy regarding mobile security. As you can see in their response to my bug report, their feelings are that you have no expectation of security if you lose your phone. I would agree that if a user loses their phone it’s in their best interest to assume all data has been compromised and respond accordingly, but I also feel developers have a duty to their users to make reasonable efforts at keeping the user’s data secure.
1Password is an example of a application whose developers take their users’ security seriously. Even if an attacker gains full access to your phone and you have 1Password on it, they won’t be able to access any sensitive information from looking in the app bundle because it’s all encrypted. Google made the effort to encrypt passwords, I’m not sure why they wouldn’t bother to secure everything. This is especially troublesome because one of the best features of Chrome is to have all of your data synced across all of your devices. If a company is storing all of my data, I just expect that they’ll make every effort to secure it. Google isn’t alone either. After noticing that a popular Craig’s List app saves the user’s username and password in plaintext, I emailed their support. Over three months later, I haven’t heard anything back from them and they’ve released an update since then, but it didn’t fix this problem. If you’re storing a user’s sensitive data, please ensure you’re doing so responsibly and securely. If you’re a user storing sensitive data in your apps, make sure you check to see how secure that data is.
Sensitive Company DataSometimes information goes into your app bundles that is meant to be kept private from others, including your users. One specific example of such information is API tokens for services like Facebook and Twitter. A few months ago I found that eBay’s iPhone app actually had their Twitter and Facebook API tokens stored in plaintext in the app. Such practices are against (at least with Twitter) their terms and conditions. A malicious party could use those tokens to spam or otherwise abuse their APIs and get your valid token revoked, breaking Twitter and Facebook functionality for any of your apps or services that use it. eBay fixed this problem in a recent update and I haven’t yet come across another app doing it, but it’s certainly something to be mindful of. Sometimes you may also come across debug or test files in an app that developers forgot to remove before shipping.
A few months back I was looking for a way to launch into a specific Pandora station using Launch Center (now Launch Center Pro). Looking in Pandora.app at the Info.plist, we can see in the CFBundleURLSchemes section that Pandora has 3 custom URL schemes: pandora, pandorav2, and pandorav3. Unfortunately this doesn’t tell us any of the arguments that you can pass to those URLs to perform more complex tasks, and when I emailed Pandora they were unable to provide any additional information. Fortunately for us a helpful developer left behind a test page in the app bundle. If you look in Pandora.app there is a file called testAdWebPage.html. Opening the file in a text editor, we find exactly what we’re looking for near the end of the page: “pandorav2:/createStation?stationId=” + stId. All you need is the station ID to plug in. You can find this by going to pandora.com and clicking on the station you want an ID for. The station ID will be at the end of the URL. Now with a URL like pandorav2:/createStation?stationId=165870829537384993, you can launch Pandora directly into a specific station. If you have test pages or images or scripts in your project, make sure you clean out anything you don’t want users to see.
As you can see, it’s not hard to find apps that have sensitive data stored with varying levels of insecurity and severity. It’s a good idea to look at your app bundles before shipping to make sure there isn’t anything in there that you don’t want people to see. If your app is storing sensitive user information, make sure you’re securing it; you owe it to your users.
It’s important to note that if you have a passcode set on your device, you won’t be able to use a tool like PhoneView until you enter the passcode. Obviously it’s a good idea to have a passcode set on your device so if a malicious person does gain access to your device, that will provide some type of barrier between them and your data. Sadly not all users choose to use a passcode, and even for those of us that do, it’s not a guarantee. The best thing to do as a developer is assume that security (or your users) will fail, and plan for the worst. You don’t want to be the weakest link in the chain.