Mark L. Murphy's Blog, page 68
March 5, 2013
SSL on Android: About That Man in the Middle
(In thanks to The Guardian Project for some assistance in assembling this material, I am blogging a portion of The Busy Coder’s Guide to Android Development Version 4.6 that covers SSL on Android. This is the second part of a four-part series. The other parts include:
The Basics
Note that if you are reading this in late 2013 or beyond, the material in the book may be newer than these blog posts.)
Man-in-the-middle attacks are a common way of trying to intercept SSL encrypted communications. The “man” in the “middle” might be a proxy server, a different Web site you wind up communicating with via DNS poisoning, etc. The objective of the “man” is to pretend to be the actual Web site or Web service you are trying to communicate with. If your app “falls for it”, your app will open an encrypted channel to the attacker, not your site, and the attacker will have access to the unencrypted data you send over that channel.
Unfortunately, Android apps have a long history of being victims of man-in-the-middle attacks.
Fahl, Harbach, Muders, and Smith of the Leibniz University of Hannover, and Baumgärtner and Freisleben of the Philipps University of Marburg, conducted research on this problem. Their results, as reported in their paper, “Why Eve and Mallory Love Android: An Analysis of Android SSL (In)Security”, is depressing. One in six surveyed apps explicitly ignored SSL certificate validation issues, mostly by means of do-nothing TrustManager implementations as noted above. Out of a selected 100 apps, 41 could be successfully attacked using man-in-the-middle techniques, yielding a treasure trove of credit card information, account credentials for all the major social networks, and so forth.
Their paper outlines a few ways in which apps can screw up SSL management – the following sections outline some of them.
Disabling SSL Certificate Validation
As mentioned above, if you disable SSL certificate validation, by implementing and using a do-nothing TrustManager, you are wide open for man-in-the-middle attacks. A simple transparent proxy server can pretend to be the real endpoint – apps ignoring SSL validation entirely will trust that the transparent proxy is the real endpoint and, therefore, perform SSL key exchange with the proxy rather than the real site. The proxy, as a result, gets access to everything the app sends.
Ignoring Domain Names
A related flaw is when you disable hostname verification. The “common name” (CN) of the SSL certificate should reflect the domain name being requested. Requesting https://www.foo.com/something and receiving an SSL certificate for xkcdhatguy.com would be indicative of a mis-configured Web server at best and a man-in-the-middle attack at worst.
By default, this is checked, and if there is no match, you will get errors like:
text
javax.net.ssl.SSLException: hostname in certificate didn't match: <...>
where the ... is replaced by whatever domain name you were requesting.
But some developers disable this check. Perhaps during development they were accessing the server using a private IP address, and they were getting SSLExceptions when trying to access that server. It is very important to allow Android to check the hostname for you, which is the default behavior.
Hacked CAs
The truly scary issue is when the problem stems from the CA itself.
Comodo, TURKTRUST, and other certificate authorities have been hacked, where nefarious parties gained the ability to create arbitrary certificates backed by the CA. For example, in the TURKTRUST case, Google found that somebody had created a *.google.com certificate that had TURKTRUST as the root CA. Any browser – or Android app – that implicitly trusted TURKTRUST-issued certificates would believe that this certificate was genuine. This is the ultimate in man-in-the-middle attacks, as code that is ordinarily fairly well-written will believe the CA and therefore happily communicate with the attacker.
In upcoming posts, we will discuss some things you can do to deal with hacked CAs or other MITM attacks.
March 4, 2013
SSL on Android: The Basics
(In thanks to The Guardian Project for some assistance in assembling this material, I am blogging a portion of The Busy Coder’s Guide to Android Development Version 4.6 that covers SSL on Android. This is the first part of a four-part series. Note that if you are reading this in late 2013 or beyond, the material in the book may be newer than these blog posts.)
The traditional approach to securing HTTP operations is by means of SSL. Android supports SSL, much as ordinary Java does. Most of the time, you can just allow Android to do its thing with respect to SSL, and you will be fine. However, there may be times when you have to play a more direct role in SSL communications, to handle arbitrary SSL-encrypted endpoints, or to help ensure that your app is not the victim of a man-in-the-middle attack.
Basic SSL Operation
Generally speaking, SSL “just works”, for ordinary sites with ordinary certificates.
If you use an https: URL with HttpUrlConnection, HttpClient, or WebView, SSL handshaking will happen automatically, and assuming the certificates check out OK, you will get your result, just as if you had requested an http: URL.
However, DownloadManager only recently added support for SSL. Originally, requesting a download via DownloadManager with an https: scheme would result in java.lang.IllegalArgumentException: Can only download HTTP URIs. As of Android 4.0, SSL is supported. Hence, you need to be careful about making SSL requests via DownloadManager to ensure that you are only doing that on a relatively recent version of Android.
Certificate Verification
The first challenge comes in verifying the SSL certificate.
You can roughly divide SSL certificates into three types:
Those issued by a certificate authority (CA) that is recognized by Android (e.g., VeriSign) or was issued by a downstream CA whose upstream CA is one recognized by Android
Those issued by a CA that is not recognized by Android
Self-signed certificates, whether used temporarily (e.g., during development) or in production
Android can only transparently handle the first set, where the root CA for the certificate is one recognized by Android. And, for better and for worse, the roster of CAs recognized by Android varies between OS versions, as Google updates the OS cacerts roster.
If you encounter an SSL certificate that cannot be verified by Android, you will get a javax.net.ssl.SSLException: Not trusted server certificate exception from HttpUrlConnection and HttpClient, and you will need to decide for yourself how to handle that.
Custom TrustManager
The right solution is to build your own TrustManager that implements your business policies.
For example, if you want to validate a self-signed SSL certificate, you can implement a TrustManager that does so, by having a custom TrustStore. A TrustStore is a set of certificates (from a CA or self-signed) that a TrustManager can validate against. Nikolay Elenkov has an excellent writeup and sample code of implementing such a TrustStore. He also demonstrates how to have a composite TrustManager, one that uses the system’s TrustManager and your own (e.g., configured with your custom TrustStore), so certificates that are validated by either TrustManager are considered to be valid.
If you are trying to use this technique to validate certificates from a CA that is not recognized by Android, you may need to use Mr. Elenkov’s technique with multiple certificates, representing the upstream chain to the root CA.
Wildcard Certificates
Some certificates are difficult to validate, because they use wildcards.
For example, Amazon S3 is a file storage and serving “cloud” solution from Amazon.com. They allow you to define “buckets” containing “objects”, where each object then has its own URL. That URL is based on the name of the bucket and the name of the object. One option is for you to have the domain name of the URL be based on the name of the bucket, leaving the path to be solely the name of the object. This works, even with SSL, but Amazon needed to use a “wildcard SSL certificate”, one that matches *.s3.amazonaws.com, not just a single domain name. By default, this will fail on Android, as Android’s stock TrustManager will not validate wildcards for multiple domain name segments (e.g., http://misc.commonsware.com.s3.amazon...). You will get an exception akin to:
text
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException:
No subject alternative DNS name matching misc.commonsware.com.s3.amazonaws.com found
However, you could write a WildcardTrustManager or some such that relies on the system TrustManager to validate the rest of the certificate, while you validate the domain name matches the expectated value. The OpenDJ project has a series of available TrustManager implementations, including one that supports wildcards.
Anti-Pattern: Disabling SSL Certificate Validation
You will find various blog posts, StackOverflow answers, and the like that suggest that you simply disable SSL certificate validation, by implementing an “accept-all” TrustManager. Such a TrustManager basically implements the interface with empty stubs for methods like checkServerTrusted(), not throwing any exceptions.
Technically, this works. And, if you are using this only early on in development and if you swear upon a stack of $RELIGIOUS_TEXTS that you will replace this hack by the time you go to production, it is difficult to complain about this technique.
However, in production, ignoring SSL certificate validation errors opens your app up to man-in-the-middle attacks.
February 27, 2013
PSA: Get Your Maps V1 Keys Now!
The Maps V1 “API Key Signup” page still reports that new API keys will be unavailable starting on March 3rd.
If you might get into Google Maps integration in the future, you may want to go ahead and get API keys for your debug and production keystores, just so that you have them. Otherwise, if Google follows through on their threat, you will be locked out of Maps V1 in a few days, as you will be unable to obtain an API key at that time.
While you will still be able to use Maps V2, there is little question that Maps V2 is still a bit rough around the edges at this point, and it is not available for many older devices. Of course, you will also have the option of using maps from other sources, such as OpenStreetMap.
For more on this issue, please seethis prior blog post.
February 21, 2013
Android 4.2 for Kiosk Apps
One of the extremely annoying questions that appears from time to time in StackOverflow is “how do I block the HOME/BACK/RECENTS button”? Usually, this is in the context of people trying to write a “kiosk mode” app for a single-purpose device, where the only thing the user should do is use the “kiosk mode” app.
The good news is that as of Android 4.2, script-kiddie hacks for this are no longer necessary.
I was somewhat bummed by the switch to the status bar/navigation bar pattern for tablets in 4.2, as seen on the Nexus 10, due to the wasted screen space. But it does have one big advantage for kiosk apps: nothing else goes there (except a MENU affordance for legacy apps).
Hence, simply making your kiosk app be the home screen solves your navigation bar problems:
Pressing HOME simply brings up your app
Pressing BACK only navigates within your app
Pressing RECENTS, while pointless, does not let anyone get anywhere other than your one-and-only app
Pressing the MENU affordance, if there is one, just brings up your legacy options menu
By going with a Fullscreen theme, access to the notification drawer and things tucked away there (e.g., access to settings), goes away.
Now, this approach may not work for all Android tablets, as some device manufacturers could tinker with the navigation bar and add their own stuff there. But there will be a number of devices, Nexuses (Nexii?) among them, where your kiosk app will work just fine.
I hope that over time this will reduce the number of people trying to find security holes in Android to arrange to block access to navigation buttons, as if kiosk apps can do it, malware will surely follow. This way, only the home screen gets this “power”, and the user has to opt into making it their home screen, and can always revert to the stock home screen via a safe-mode reboot.
February 19, 2013
The Busy Coder's Guide to Android Development Version 4.6 Released
Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 4.6, in all formats. Just log into your Warescription page and download away, or set up an account and subscribe!
This release… does not add quite as much as I had hoped for. That being said, it includes:
New coverage of “jank”-busting, including coverage of quantifying jank using gfxinfo and systrace
Coverage of using SSL, including trying to defeat man-in-the-middle (MITM) attacks
Expanded coverage of Maps V2
A bit more coverage of locations (this chapter will undergo a series of improvements over the next few releases)
Fixes lots of errata and adds other minor improvements
In terms of the book formats, I have constrained the maximum image height on the PDF versions, to reduce the number of pages that have a screenshot+caption and nothing else.
Version 4.7 is planned for early April.
February 18, 2013
WebView, addJavascriptInterface(), and Android 4.2 Change
TL;DR: Add @JavascriptInterface to any method you plan on exposing via addJavascriptInterface() in a WebView, to ensure long-term compatibility for your project.
The aforementioned change will be very important for apps whose android:targetSdkVersion is set to 17 or higher, running on Android 4.2+ devices. Methods without the @JavascriptInterface annotation will not be accessible from JavaScript.
This is for security reasons. Previously, any public method on the injected Java object would be reachable via JavaScript. Java classes usually have many inherited methods public methods, including ones like getClass(). JavaScript could then use reflection to access other stuff, beyond what you had in mind. By limiting access to @JavascriptInterface methods, only those you specifically opt into being accessible will be usable from the JavaScript environment.
The good news is that this improves security.
The bad news is that I missed hearing about this change, until this blog post showed up.
This change — a regression by some measures — was not mentioned in the Android 4.2 release notes. Also, the API differences report does not make this change obvious. Since the method signature for addJavascriptInterface() did not change, it does not show up in the report. While WebView itself does, the change in behavior is not mentioned in the class-level JavaDocs. Only if you read the documentation for the “un-modified” addJavascriptInterface(), or if you caught mention of the new JavascriptInterface annotation in the API differences report, would you have realized that this change was made.
Ideally, regressions like this would make it into the Android 4.2 release notes, as did the change in default ContentProvider exported status, the changes to user location accuracy, and the changes to some Settings.System read-write status, all of which were covered in the release notes.
February 15, 2013
App Quality Alliance's Resources for Android Developers
The App Quality Alliance publishes several resources for quality assurance that may be of interest to Android developers.
Their Android Testing Criteria document has, at this time, ~80 tests that they feel should be run to confirm that an app gives a quality user experience, from:
Ensure that the Application notifies the user about a long launch time
to:
If the user accepts an incoming phone call while the Application is running, it should be possible to resume from the same point in the Application at the end of the call, or a logical re-starting point.
Each test comes with a series of steps, designed for a tester to follow to determine if the test passes. This document can be found on the site’s “Docs, FAQs, and Other Info” page, or you can download the February 2013 edition of the PDF directly.
They also publish the Quality App Directory and badge program, for apps certified to pass those tests.
In addition, they have a “best practices” guide for mobile app developers and a quick “top 10” errors list
The documents are definitely worth a read and could easily form part of a developer’s release checklist.
January 30, 2013
Maps V2 May Need ACCESS_NETWORK_STATE Permission
The Google Maps V2 documentation has a section describing the permissions needed to use Maps V2 in your app.
However, there is evidence that this list is missing one entry: ACCESS_NETWORK_STATE. The stack trace shown in this StackOverflow question clearly shows Maps V2 code calling getActiveNetworkInfo(), which definitely needs ACCESS_NETWORK_STATE.
Now, most of the time, Maps V2 does not call getActiveNetworkInfo(). I have never seen a crash in the lab for missing ACCESS_NETWORK_STATE, and neither has the author of the StackOverflow question. But, it would appear that there is some set of conditions whereby Maps V2 does call getActiveNetworkInfo(). Probably, therefore, this is a documentation bug, though there’s a slight chance that the bug is that Maps V2 should not be calling getActiveNetworkInfo() at all.
Since the Maps V2 code is coming from an Android library project attached to your project, and since at the time of this writing (AFAIK) there has only been one release of this project, this issue may affect all apps using Maps V2.
Whether you go ahead and add ACCESS_NETWORK_STATE now, or you wait to see what happens with the associated Google Maps issue, is up to you. The more permissions you request, the more likely it is that some prospective user will skip your app. However, crashing under some unknown circumstances because you lack this permission is not exactly a great answer, either.
(and mad props to Tristan Waddington for posting the StackOverflow question!)
January 29, 2013
What Not To Put In Your Repo
The recent flurry of concern about GitHub searches turning up RSA keys and the like is really nothing new. We have always needed to ponder what files we actually put into our public repositories, whether those are GitHub or something else. The GitHub escapades simply heightened awareness of this issue for a while.
So, what specific things native to Android application development should you avoid publishing in public, such as an open GitHub repo? Here are three categories to consider:
Your Keystores
Your signing keystores should not be published for all comers, particularly your production keystore. If others have access to your production keystore, all they need to do is hack your Google account, and they can then replace your app with something else that you, and your users, probably do not want.
Collaborative open source projects for creating published Android apps will need to strike a balance. Ideally, the production keystore is known by more than one person, so one disgruntled developer cannot stymie publishing updates. However, allowing everyone to get to the production keystore is dangerous. Have some out-of-repo way of getting the keystore to trusted team members, possibly involving some sort of formal ceremony involving swords and stuff.
Your API Keys (Except…)
If you have signed up for some Web service that issues an API key, and that API key is not tied to your keystores, you do not want to have that API key in your repo. Others can use that API key to masquerade as you, performing operations with the Web service on your behalf.
The Google Maps V1 and Maps V2 APIs for Android have API keys, but those keys are tied to your signing key. The API keys are useless unless they are employed by an app signed by the signing key tied to the API key. Hence, these are safer to release into a repo, so long as you feel confident that you have not leaked the associated signing keys.
If you need API keys in your open source app, and you do not want them in your repo, one pattern is to store them in a dedicated string resource file.
For example, I use ACRA in conjunction with BugSense for some apps, until I get around to hosting my own ACRA server component. BugSense uses a API key for tying ACRA to BugSense, and their sample code looks something like this:
@ReportsCrashes(formUri = "http://www.bugsense.com/api/acra?api_...", formKey="")
While that works, using an ACRA annotation pretty much forces you to put your API key right in the source code.
An alternative, for recent versions of ACRA, is to do this configuration at runtime:
public class AcraApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (!BuildConfig.DEBUG) {
ACRAConfiguration config=ACRA.getNewDefaultConfig(this);
config.setFormUri(getString(R.string.acra_form_uri));
ACRA.setConfig(config);
ACRA.init(this);
}
}
Here, the formUri is set in Java code, from a string resource. You can keep that string resource in its own file (e.g., res/values/acra.xml) and exclude that individual file from the repo (e.g., via .gitignore). Include in the project README a note of what needs to go in this file, so that those checking out the project know why it will not build cleanly out of the box.
Your Personal Information
Be judicious about putting contact information in your repo that might be used for spam, phishing, etc. Information that is already readily available from other sources is not a big deal (e.g., an email address you use for business). You might have some of this in your app in an about dialog, or some form of technical support documentation, or the like.
January 23, 2013
No, Android Does *Not* Have a Crop Intent
Many developers are calling startActivity() on an Intent with an action of com.android.camera.action.CROP. They are doing this to crop an image.
This is a really bad idea.
Any time you find yourself typing or pasting something starting with com.android, warning bells should be sounding. Better yet, the “Lost in Space” Robot should amble toward you, waving his arms, shouting “Danger, Will Robinson! Danger!”
(note: while your name may not be Will Robinson, if a robot is flailing about in your vicinity, just play along, m’kay?)
In this specific case, this Intent action is supported by the AOSP Camera app. That app does not exist on all devices. Devices lacking this app will not respond to this undocumented Intent action, and your app will crash.
There are several open source libraries for cropping images in Android. I have not tried any of them, and therefore cannot vouch for how well any work. However, they are safer bets than relying upon an undocumented Intent action from an app that may not exist on any given user’s device.
Here are some libraries to consider:
https://github.com/lvillani/android-cropimage
https://github.com/biokys/cropimage
https://github.com/MMP-forTour/cropimage (forked from the above one)
https://github.com/dtitov/pickncrop


