谈谈移动应用的安全性实践

作为一家大数据公司,Glow不仅重视用户的数据,更加注重数据的安全性。

本文将从用户注册流程出发,逐步介绍我们在提高数据安全性方面采用的一些策略方法,供读者参考。下面将从 Android服务端 两部分来进行讲解。

从注册说起

用户第一次打开app时便会进入注册页面,然后客户端会要求用户输入用户名、密码并传递给服务端去创建一个新的user。此时若通过明文传递用户名密码便是一个安全性隐患。或者说,如果有人监听注册API,那么很快就可以窃取到很多用户的账户数据,而且可以偷偷利用这些账户信息随时获取甚至更改用户数据。

这对于任何一家企业而言都是非常可怕的。

全站Https

因此,为了应对数据明文传输隐患这个问题,我们在自家所有app中都采用了Https方式通信。在Android端我们采用了Square家的OkHttp3作为网络层,为应用层提供Https服务。

下面先对Https的基本工作原理进行下介绍,以便后文的讲解。

  • 1、首先,客户端去请求服务端的数字证书,这个证书包含了一个公钥。该证书购买后存储于我们自己服务器上。

  • 2、当服务端收到客户端请求后,会把这个数字证书回传给客户端,由于是公钥,所以不害怕被窃取。

  • 3、客户端收到数字证书后,先去验证证书的真实性。如果验证通过,就会从里面取出一个公钥。

  • 4、客户端本地生成一个随机数,作为未来的会话私钥,利用前面的公钥进行加密。

  • 5、客户端把加密后会话私钥回传给服务端,在这个过程中,即使加密后的会话私钥被窃取也不用担心,因为中间人并没有解密私钥,所以读不出里面的会话私钥。

  • 6、服务端接收到加密会话私钥后,利用从CA购买证书时获得的解密私钥进行解密读出真实会话私钥。至此,客户端与服务端同时拥有了一个只有它们二者知道的会话私钥,非对称加密连接建立完成。

  • 7、一旦客户端和服务端连接建立起来后,未来的数据通信都利用这个会话私钥进行对称加密传输数据。

采用了https后,我们所有网络传输的数据都由明文变成了密文,即使中间有人能够监听到数据包,也不能轻易获取user的帐户密码信息。

听起来,安全性问题基本解决了。然而实际上,在步骤3用户需要去验证数字证书时,如果这个验证过程被欺骗了呢?

试想这样一种场景,如果在最开始,攻击者就拦截掉客户端与服务端的通信。当客户端在请求证书时,攻击者回传一个他自己的假证书,而且攻击者已经通过其他手段欺骗用户在手机上信任了这个假证书,那么当客户端接收到证书并去验证时,是可以通过的。

这也就意味着,一旦客户端遭受这样的攻击,未来客户端都会与一个虚假的中间人通信,而且中间人也可以拿着客户端传来的信息去与我们的服务端通信,而这个过程客户端和我们服务端完全不知道中间人的存在,这是很大的安全隐患。

公钥绑定(SSL Pinning)

为了防止客户端被虚假证书欺骗,可以采取的方式是把我们自己的公钥直接绑定给客户端,当客户端收到证书后,与提前绑定好的公钥进行验证,从而防止虚假证书的入侵。

在Android端,我们利用OkHttp3提供的CertificatePinner实现公钥绑定

  • OkHttpClient client = new OkHttpClient.Builder()
  • .certificatePinner(
  • new CertificatePinner.Builder().add("your_host", "your_public_key").build())
  • .build();

至此,我们可以利用更为安全的https协议来传输用户名和密码,我们继续来看上面的注册流程。

Token机制

回到注册流程。当服务端拿到用户名密码后,会去创建一个新的user,同时我们会基于用户相关信息生成一个Token并回传给客户端。客户端在接收到Token后需要在本地进行存储。另外,由于每个http请求都是无状态的,因此未来客户端如果想把user信息传递给服务端时,就必须通过Token来传递,才能识别出某个请求的来源。

那么,我们应该如何在Android和服务端的代码里具体实现Token的传递解析有效性验证机制呢?

1. 首先在Android端,为了把Token信息存入到所有请求的http header里,我们采用了okhttp3提供的interceptor接口来。

  • OkHttpClient client = new OkHttpClient.Builder()
  • .addInterceptor(new Interceptor() {
  • @Override
  • public Response intercept(Chain chain) throws IOException {
  • Request request = chain.request();
  • Request.Builder newRequestBuilder = request.newBuilder();
  • String token = getAuthToken();
  • if (!TextUtils.isEmpty(token)) {
  • newRequestBuilder.addHeader("Authorization", token);
  • }
  • Request newRequest = newRequestBuilder.build();
  • return chain.proceed(newRequest);
  • }
  • })
  • .build();

2. 然后在服务端,我们需要解析客户端传递过来的Token信息并进行校验。这里可以创建一个pythondecorator方法:
```
def mobile_request(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
kwargs = kwargs if kwargs or {}

top Created with Sketch.