Securing Mobile Apps — Part 3: Mobile Application Network Security -Retrofit, REST API

Jay Dwivedi
4 min readMay 19, 2024

--

Network security is a critical aspect of mobile app security, ensuring that data transmitted between the app and the server is secure from eavesdropping and tampering. This section focuses on securing network communications in Android applications using Retrofit, OkHttp, and various security mechanisms such as interceptors, certificate pinning, JWT token authentication, and digital certificates.

Authentication and Authorization

  • Strong Authentication: Implement multi-factor authentication (MFA) to add an extra layer of security beyond just passwords.
  • Role-Based Access Control (RBAC): Ensure users have access only to the resources they need, minimizing the risk of unauthorized access.

JWT Tokens

  • Secure Token Generation: Use secure libraries to generate JWT tokens and ensure they are signed with strong algorithms (e.g., HS256 or RS256).
  • Token Storage: Store tokens securely on the client side, using secure storage mechanisms (e.g., Keychain for iOS, EncryptedSharedPreferences for Android).
  • Token Expiry: Implement short-lived tokens and refresh tokens to minimize the risk if a token is compromised.

Hashing and Digital Signatures

  • Hashing: Use strong hashing algorithms (e.g., SHA-256) to hash sensitive data before transmission.
  • Digital Signatures: Use digital signatures to ensure the integrity and authenticity of messages. This can be done using asymmetric encryption algorithms like RSA or ECC.

Certificate Pinning

  • Implement Certificate Pinning: Pin your app to a specific server certificate or public key to prevent man-in-the-middle (MITM) attacks.
  • Libraries and Tools: Use libraries like TrustKit for iOS and OkHttp for Android to implement certificate pinning easily.

API Call Security and Handshaking

  • TLS/SSL: Use TLS (Transport Layer Security) to encrypt data in transit and protect against eavesdropping and tampering.
  • Mutual TLS (mTLS): Implement mutual TLS to authenticate both the client and the server, adding an extra layer of security.
  • Rate Limiting and Throttling: Implement rate limiting and throttling to protect APIs from abuse and Denial-of-Service (DoS) attacks.

1. Retrofit and OkHttp3

Introduction to Retrofit

Retrofit is a type-safe HTTP client for Android and Java, developed by Square. It simplifies network operations and allows for seamless integration with REST APIs. OkHttp is the underlying library used by Retrofit to handle network operations.

Add Retrofit and OkHttp3 to your project dependencies:

dependencies {
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
}

Setting Up Retrofit

Set up Retrofit with a base URL and a Gson converter for JSON parsing:

val retrofit = Retrofit.Builder()
.baseUrl("https://yourapi.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
.build()

val apiService = retrofit.create(ApiService::class.java)

Creating OkHttpClient

Configure OkHttpClient with an interceptor for logging and JWT token authentication:

fun createOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.addInterceptor(AuthInterceptor())
.certificatePinner(createCertificatePinner())
.build()
}

2. Interceptors

Logging Interceptor

Add a logging interceptor to log network requests and responses. This is useful for debugging but should be disabled in production:

val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

Authentication Interceptor

Create an interceptor to add the JWT token to the headers of every request:

class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val requestWithAuth = originalRequest.newBuilder()
.header("Authorization", "Bearer ${getJwtToken()}")
.build()
return chain.proceed(requestWithAuth)
}

private fun getJwtToken(): String {
// Retrieve JWT token from secure storage
return "your_jwt_token"
}
}

3. Certificate Pinning

Certificate pinning helps protect against man-in-the-middle (MITM) attacks by ensuring the app communicates only with servers that present a specific certificate or public key.

Setting Up Certificate Pinning

Configure OkHttpClient with certificate pinning:

fun createCertificatePinner(): CertificatePinner {
return CertificatePinner.Builder()
.add("yourapi.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build()
}

4. JWT Token Authentication

Securely Storing JWT Tokens

Store JWT tokens securely using EncryptedSharedPreferences:

val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()

val sharedPreferences = EncryptedSharedPreferences.create(
context,
"auth_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

val editor = sharedPreferences.edit()
editor.putString("jwt_token", "your_jwt_token")
editor.apply()

API Call with JWT Authentication

Define a Retrofit API service interface:

interface ApiService {
@GET("endpoint")
suspend fun getData(): Response<DataModel>

@POST("login")
suspend fun login(@Body credentials: Credentials): Response<AuthToken>
}

Make an authenticated API call:

suspend fun fetchData() {
val response = apiService.getData()
if (response.isSuccessful) {
val data = response.body()
// Handle the data
} else {
// Handle error
}
}

5. Hashing and Digital Certificates

Hashing Sensitive Data

Hash sensitive data such as passwords before sending them to the server:

fun hashPassword(password: String): String {
val digest = MessageDigest.getInstance("SHA-256")
val hashBytes = digest.digest(password.toByteArray(Charsets.UTF_8))
return hashBytes.joinToString("") { "%02x".format(it) }
}

Using Digital Certificates

Use digital certificates for mutual SSL/TLS authentication. This ensures that both the client and server authenticate each other.

Adding Certificates to Keystore

Import the certificate into the Android KeyStore:

val certificateFactory = CertificateFactory.getInstance("X.509")
val certificate = context.resources.openRawResource(R.raw.server_cert).use {
certificateFactory.generateCertificate(it)
}

val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
load(null)
setCertificateEntry("server", certificate)
}

Configuring OkHttpClient for Mutual TLS

val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
keyManagerFactory.init(keyStore, null)

val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(keyStore)

val sslContext = SSLContext.getInstance("TLS").apply {
init(keyManagerFactory.keyManagers, trustManagerFactory.trustManagers, SecureRandom())
}

val client = OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustManagerFactory.trustManagers[0] as X509TrustManager)
.build()

Conclusion

Securing network communications in Android applications is essential for protecting user data and ensuring the integrity of data transmitted between the app and the server. By using Retrofit and OkHttp3 with interceptors, certificate pinning, JWT token authentication, and digital certificates, you can create a robust and secure network communication layer for your mobile app. Implementing these best practices and leveraging the provided Kotlin code examples will significantly enhance the security of your mobile applications.

To read about All Application security Topics refer this :

Thanks for reading.

Keep learning!!!

Subscribe and clap if like.

--

--

Jay Dwivedi

Currently working as Lead Android Developer and offering excellent quality Project • Experienced in the following technologies: Android, Java, Kotlin Flutter,