Securing Mobile Apps — Part 3: Mobile Application Network Security -Retrofit, REST API
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.