Mattatyahu
Mattatyahu

Reputation: 71

How avoid man-in-the-middle attack when using Google Cloud Endpoints?

I'm currently writing my first android app and decided to use google cloud endpoints. I have secure my backend endpoints methods by following this docs : https://cloud.google.com/appengine/docs/java/endpoints/auth, https://cloud.google.com/appengine/docs/java/endpoints/gen_clients, https://cloud.google.com/appengine/docs/java/endpoints/consume_android

Basically you have to generate WEB_CLIENT_ID and ANDROID_CLIENT_ID for your endpoints, add a param User on each method you want to secure and finally check inside the method that the user is not null otherwise throw OAuthRequestException.

I have build the client android library and configure it like this in my android app :

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String accountName = preferences.getString(MainActivity.PREF_ACCOUNT_NAME, null);

GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(context,
                        "server:client_id:xxxxxxxxx-xxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com");

credential.setSelectedAccountName(accountName);

DataLoaderApi.Builder builder = new DataLoaderApi.Builder(AndroidHttp.newCompatibleTransport(),
                        new AndroidJsonFactory(), credential);

At first it seems to work pretty well you cannot use the backend methods without credential. (You will have 401 error)

Unfortunately I notice that it's still possible to decrypt https request/response to backend endpoints if you use a tool like "Charles Proxy" (https://www.charlesproxy.com/). You just have to install charles proxy certificate in your smartphone and configure the proxy.

Here's another description of the same problem: http://nickfishman.com/post/50557873036/reverse-engineering-native-apps-by-intercepting-network

Now you have the endpoints url used by the android app, you've got all the param sent in each request and all the header properties even the autorization property with the token so basically all the informations you need to use my backend endpoints in your app. :( I know the token will expire after a while but in the meantime you can extract my datas.

Google speak about that in the google cloud store doc : https://cloud.google.com/storage/docs/concepts-techniques#bestpractices

"Make sure that you use an HTTPS library that validates server certificates. A lack of server certificate validation makes your application vulnerable to man-in-the-middle attacks or other attacks."

Great but when you use the endpoints client library generated with their tool you are vulnerable too.

I know there are solution to avoid this problem: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning

But how can I use the generated endpoints client android library and avoid man-in-the-middle attack ?

Upvotes: 2

Views: 916

Answers (1)

sandeepd
sandeepd

Reputation: 515

Create your custom KeyStore and put only system certs in it. Android system certs can be obtained from here.

Create TrustManager and SSLSocketFactory which will trust only this KeyStore which you've just created, referenced from this:

// Load CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
    ca = cf.generateCertificate(caInput);
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
    caInput.close();
}

// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

SSLSocketFactory ssf = context.getSocketFactory();

Create NetHttpTransport using NetHttpTransport.Builder, and set created SSLSocketFactory above. Then you can use your endpoint method builder as usual:

Api.Builder builder = new Api.Builder(objNetHttpTransport,
                    new AndroidJsonFactory(), credential);

This should work, only prob is in managing root certs. You will need to send updates regularly.

Upvotes: 0

Related Questions