2

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 ?

4

1 回答 1

0

创建您的自定义 KeyStore 并仅将系统证书放入其中。Android 系统证书可以从这里获得。

创建 TrustManager 和 SSLSocketFactory ,它们将只信任您刚刚创建的 KeyStore,引用

// 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();

使用 NetHttpTransport.Builder 创建 NetHttpTransport,并在上面设置创建的 SSLSocketFactory。然后你可以像往常一样使用你的端点方法构建器:

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

这应该可行,只有问题在于管理根证书。您需要定期发送更新。

于 2017-12-18T11:44:51.850 回答